mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-18 04:36:30 +01:00
merge r1449 from surfaces branch to include mackie surface and tranzport updates. Thanks to Gerd Flaig for the merge command: svn merge svn+ssh://ardoursvn@ardour.org/ardour2/trunk@1449 svn+ssh://ardoursvn@ardour.org/ardour2/branches/surfaces.
git-svn-id: svn://localhost/ardour2/trunk@1460 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
92c09a6d91
commit
e878b36519
117 changed files with 16565 additions and 46 deletions
15
SConstruct
15
SConstruct
|
|
@ -311,7 +311,7 @@ env.Append (BUILDERS = {'VersionBuild' : version_bld})
|
|||
#
|
||||
|
||||
def versioned_builder(target,source,env):
|
||||
w, r = os.popen2( "svn info | awk '/^Revision:/ { print $2}'")
|
||||
w, r = os.popen2( "LANG= svn info | awk '/^Revision:/ { print $2}'")
|
||||
|
||||
last_revision = r.readline().strip()
|
||||
w.close()
|
||||
|
|
@ -660,7 +660,8 @@ opt_flags[:0] = [
|
|||
"-fomit-frame-pointer",
|
||||
"-ffast-math",
|
||||
"-fstrength-reduce",
|
||||
"-fno-strict-aliasing"
|
||||
"-fno-strict-aliasing",
|
||||
"-pipe"
|
||||
]
|
||||
|
||||
if env['DEBUG'] == 1:
|
||||
|
|
@ -950,7 +951,7 @@ else:
|
|||
# its included in the tarball
|
||||
#
|
||||
|
||||
surface_subdirs = [ 'libs/surfaces/control_protocol', 'libs/surfaces/generic_midi', 'libs/surfaces/tranzport' ]
|
||||
surface_subdirs = [ 'libs/surfaces/control_protocol', 'libs/surfaces/generic_midi', 'libs/surfaces/tranzport', 'libs/surfaces/mackie' ]
|
||||
|
||||
if env['SURFACES']:
|
||||
if have_libusb:
|
||||
|
|
@ -1061,6 +1062,13 @@ if not conf.CheckFunc('posix_memalign'):
|
|||
env = conf.Finish()
|
||||
|
||||
rcbuild = env.SubstInFile ('ardour.rc','ardour.rc.in', SUBST_DICT = subst_dict)
|
||||
subst_dict['%VERSION%'] = ardour_version[0:3]
|
||||
subst_dict['%EXTRA_VERSION%'] = ardour_version[3:]
|
||||
subst_dict['%REVISION_STRING%'] = ''
|
||||
if os.path.exists('.svn'):
|
||||
subst_dict['%REVISION_STRING%'] = '.' + fetch_svn_revision ('.') + 'svn'
|
||||
|
||||
specbuild = env.SubstInFile ('ardour.spec','ardour.spec.in', SUBST_DICT = subst_dict)
|
||||
|
||||
the_revision = env.Command ('frobnicatory_decoy', [], create_stored_revision)
|
||||
|
||||
|
|
@ -1078,6 +1086,7 @@ env.Distribute (env['DISTTREE'],
|
|||
[ 'SConstruct', 'svn_revision.h',
|
||||
'COPYING', 'PACKAGER_README', 'README',
|
||||
'ardour.rc.in',
|
||||
'ardour.spec',
|
||||
'ardour_system.rc',
|
||||
'tools/config.guess',
|
||||
'icons/icon/ardour_icon_mac_mask.png',
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ CONFIG_VARIABLE (bool, midi_control, "midi-control", false)
|
|||
|
||||
CONFIG_VARIABLE (uint32_t, feedback_interval_ms, "feedback-interval-ms", 100)
|
||||
CONFIG_VARIABLE (bool, use_tranzport, "use-tranzport", false)
|
||||
CONFIG_VARIABLE (std::string, mackie_emulation, "mackie-emulation", "mcu")
|
||||
|
||||
/* disk operations */
|
||||
|
||||
|
|
|
|||
|
|
@ -738,7 +738,7 @@ Region::state (bool full_state)
|
|||
{
|
||||
XMLNode *node = new XMLNode ("Region");
|
||||
char buf[64];
|
||||
char* fe;
|
||||
char* fe = NULL;
|
||||
|
||||
_id.print (buf, sizeof (buf));
|
||||
node->add_property ("id", buf);
|
||||
|
|
@ -760,6 +760,10 @@ Region::state (bool full_state)
|
|||
case EditChangesID:
|
||||
fe = X_("id");
|
||||
break;
|
||||
default: /* should be unreachable but makes g++ happy */
|
||||
cerr << "Odd region property found\n";
|
||||
fe = X_("nothing");
|
||||
break;
|
||||
}
|
||||
|
||||
node->add_property ("first_edit", fe);
|
||||
|
|
|
|||
|
|
@ -3407,11 +3407,11 @@ Session::available_capture_duration ()
|
|||
|
||||
switch (Config->get_native_file_data_format()) {
|
||||
case FormatFloat:
|
||||
sample_bytes_on_disk = 4;
|
||||
sample_bytes_on_disk = 4.0;
|
||||
break;
|
||||
|
||||
case FormatInt24:
|
||||
sample_bytes_on_disk = 3;
|
||||
sample_bytes_on_disk = 3.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -404,6 +404,7 @@ Session::GlobalMeteringStateCommand::get_state()
|
|||
case MeterPostFader:
|
||||
meterstr = X_("post");
|
||||
break;
|
||||
default: meterstr = "";
|
||||
}
|
||||
|
||||
child->add_property (X_("meter"), meterstr);
|
||||
|
|
@ -430,6 +431,7 @@ Session::GlobalMeteringStateCommand::get_state()
|
|||
case MeterPostFader:
|
||||
meterstr = X_("post");
|
||||
break;
|
||||
default: meterstr = "";
|
||||
}
|
||||
|
||||
child->add_property (X_("meter"), meterstr);
|
||||
|
|
|
|||
10
libs/surfaces/frontier/kernel_drivers/BUILD
Normal file
10
libs/surfaces/frontier/kernel_drivers/BUILD
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
To build, type make
|
||||
|
||||
# make install and run
|
||||
ir: install
|
||||
rmmod tranzport
|
||||
modprobe tranzport
|
||||
|
||||
# make install, run, and run tests
|
||||
irt: ir
|
||||
|
||||
35
libs/surfaces/frontier/kernel_drivers/Makefile
Normal file
35
libs/surfaces/frontier/kernel_drivers/Makefile
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
ifneq ($(KERNELRELEASE),)
|
||||
|
||||
obj-m := tranzport.o
|
||||
tranzport-objs :=
|
||||
|
||||
else
|
||||
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
PWD := $(shell pwd)
|
||||
MODDIR := $(DESTDIR)/lib/modules/$(shell uname -r)/kernel/sound/usb/misc
|
||||
BINDIR := $(DESTDIR)/usr/local/bin
|
||||
|
||||
default::
|
||||
$(MAKE) -Wall -C $(KDIR) SUBDIRS=$(PWD) modules
|
||||
$(MAKE) -C tests
|
||||
|
||||
install-only:: default
|
||||
mkdir -p $(MODDIR) $(BINDIR)
|
||||
cp tranzport.ko $(MODDIR)
|
||||
$(MAKE) -C tests install
|
||||
|
||||
install:: install-only
|
||||
/sbin/depmod -a
|
||||
+/sbin/rmmod tranzport
|
||||
/sbin/modprobe tranzport
|
||||
|
||||
irt:: install
|
||||
tranzport_tests.sh
|
||||
|
||||
clean::
|
||||
rm -f core .*.cmd *.o *.ko *.mod.c Module.symvers *.bak .\#* *~
|
||||
rm -rf .tmp_versions
|
||||
$(MAKE) -C tests clean
|
||||
|
||||
endif
|
||||
16
libs/surfaces/frontier/kernel_drivers/README
Normal file
16
libs/surfaces/frontier/kernel_drivers/README
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
This directory contains the USB Tranzport Kernel driver for Linux.
|
||||
|
||||
At present it accepts reads/writes of 8 byte cmds to /dev/tranzport0 to control
|
||||
the lights and screen.
|
||||
|
||||
Reads are possible. Wheel Compression does not currently account for button changes
|
||||
|
||||
It also has some sysfs hooks that are non-functional at the moment.
|
||||
|
||||
The API is closely tied to the ardour revision and WILL change.
|
||||
|
||||
A sysfs interface is PERFECT for simple userspace apps to do fun things with the
|
||||
lights and screen. It's fairly lousy for handling input events and very lousy
|
||||
for watching the state of the shuttle wheel.
|
||||
|
||||
In the end this is going to be driven by a midi layer
|
||||
35
libs/surfaces/frontier/kernel_drivers/doc/keycodes.html
Normal file
35
libs/surfaces/frontier/kernel_drivers/doc/keycodes.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE> TRANZPORT KEYCODES REFERENCE </TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<H2> TRANZPORT KEYCODES REFERENCE </H2>
|
||||
|
||||
<H3>Footswitch</H3>
|
||||
|
||||
At least on every footswitch I've tried, the polarity appears to be wrong, in that the footswitch "up" position results
|
||||
in 0100 being OR'd into the result. Pressing it down results in a 0, if no other keys are pressed. Releasing it results in 0100.
|
||||
|
||||
Every other key when up results in 0. This odd behavior would hopefully be controllable via a command to the tranzport,
|
||||
but I don't have that, so dealing with footswitch events is weird.
|
||||
|
||||
So, seeing this bit enabled would be something like "HAVE_FOOTSWITCH INSTALLED", BE SMART ABOUT IT.
|
||||
|
||||
|
||||
<H3>Special Key Combinations</H3>
|
||||
<p>
|
||||
In addition to the normal keycodes generated by the tranzport, it is possible to hit several combinations of keys and get a unique
|
||||
result. Some are really weird. Perhaps the following assignments make sense:
|
||||
</p><p>
|
||||
<table><tr><th>PRESSING</th><th>RESULT</th><th>ASSIGNED TO</th></tr>
|
||||
<tr><td>TRACKLEFT+TRACKRIGHT</td><td>TRACKLEFT+TRACKRIGHT</td><td>Master</td></tr>
|
||||
<tr><td>SHIFT+TRACKLEFT+TRACKRIGHT</td><td>SHIFT+TRACKLEFT+TRACKRIGHT+UNDO</td><td>Show Bus Only Toggle</td></tr>
|
||||
<tr><td>IN+OUT</td><td>IN+OUT</td><td>Zoom 100%</td></tr>
|
||||
<tr><td>SHIFT+IN+OUT</td><td>SHIFT+IN+OUT+UNDO</td><td>Max Zoom</td></tr>
|
||||
<tr><td>SHIFT+REW+FFW</td><td>SHIFT+REW+FFW+UNDO</td><td></td></tr>
|
||||
<tr><td>RECORD+TRACKSOLO+FOOTSWITCHNOTDEPRESSED</td><td>RECORD+TRACKSOLO+BATTERY</td><td></td></tr>
|
||||
<tr><td>PLAY+MUTE</td><td>PLAY+MUTE</td><td></td></tr>
|
||||
</table>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
23
libs/surfaces/frontier/kernel_drivers/tests/Makefile
Normal file
23
libs/surfaces/frontier/kernel_drivers/tests/Makefile
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Some basic utilities for testing the tranzport's I/O
|
||||
# eventually "tranzport" will become a flexible command
|
||||
#
|
||||
#
|
||||
|
||||
FILES:=tranzport tranzport_lights tranzport_tests.sh
|
||||
BINDIR ?= $(DESTDIR)/usr/local/bin
|
||||
|
||||
all: tranzport tranzport_lights
|
||||
|
||||
tranzport: tranzport.c
|
||||
gcc -g -Wall -o tranzport tranzport.c
|
||||
|
||||
tranzport_lights: tranzport_lights.c
|
||||
gcc -g -Wall -o tranzport_lights tranzport_lights.c
|
||||
|
||||
clean::
|
||||
rm -f core .*.cmd *.o *.ko *.mod.c Module.symvers *.bak .\#* *~
|
||||
rm -rf .tmp_versions tranzport tranzport_lights
|
||||
|
||||
install::
|
||||
cp $(FILES) $(BINDIR)
|
||||
|
||||
104
libs/surfaces/frontier/kernel_drivers/tests/README
Normal file
104
libs/surfaces/frontier/kernel_drivers/tests/README
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
tranzport 0.1 <tranzport.sf.net>
|
||||
oct 18, 2005
|
||||
arthur@artcmusic.com
|
||||
---
|
||||
|
||||
The Frontier Design Tranzport(tm) (www.frontierdesign.com) is a simple
|
||||
wireless USB device. It is not a MIDI device. The document on their web
|
||||
site "Tranzport(tm) Native Mode Interface Description" describes the
|
||||
Tranzport(tm) as if it were a MIDI device, but this is implemented by their
|
||||
Windows and Macintosh software drivers.
|
||||
|
||||
This code will allow you to use your Tranzport(tm) at a lower level of
|
||||
abstraction. This code relies on libusb, which can be obtained from
|
||||
libusb.sourceforge.net.
|
||||
|
||||
To compile the program, type "make". You should end up with a executable
|
||||
called "tranzport". You'll probably have to run this program as root.
|
||||
|
||||
Using the program is straightforward. It will simply tell you which
|
||||
buttons are being pressed and what not. If you press one of the buttons
|
||||
with a light, the light will turn on. If you hold shift and press one of
|
||||
the buttons with a light, the light will turn off. If you take out the
|
||||
batteries to the device (or go out of range), it will tell you that the
|
||||
device is offline. When you replace the batteries (or come back in
|
||||
range), it should tell you it is back online.
|
||||
|
||||
Once you understand how everything works, you should be able to
|
||||
incorporate it into your own setup however you wish.
|
||||
|
||||
This code was developed on a Linux machine, but (theoretically) it
|
||||
should work on any system that is supported by libusb, since that is how
|
||||
it communicates with the device.
|
||||
|
||||
Here are a few more details about the device:
|
||||
|
||||
There are two endpoints for communication with the device. All data
|
||||
reads and writes are done in 8-byte segments.
|
||||
|
||||
One endpoint is for interrupt reads. This is used to read button data
|
||||
from the device. It also supplies status information for when the device
|
||||
goes out of range and comes back in range, loses power and regains
|
||||
power, etc. The format of the data is:
|
||||
|
||||
00 ss bb bb bb bb dd 00 (hexadecimal)
|
||||
|
||||
where:
|
||||
|
||||
ss - status code, 01=online ff=offline
|
||||
bb - button bits
|
||||
dd - data wheel, 01-3f=forward 41-7f=backward
|
||||
|
||||
Please refer to the source code for a list of the button bits.
|
||||
|
||||
The other endpoint is for interrupt writes. This is used to toggle the
|
||||
lights on the device, and to write data to the LCD.
|
||||
|
||||
There are 7 lights on the device. To turn a light on, send the following
|
||||
sequence of bytes:
|
||||
|
||||
00 00 nn 01 00 00 00 00 (hexadecimal)
|
||||
|
||||
where nn is the light number.
|
||||
|
||||
To turn a light off:
|
||||
|
||||
00 00 nn 00 00 00 00 00 (hexadecimal)
|
||||
|
||||
Here is the list of lights:
|
||||
|
||||
00 Record
|
||||
01 Track Rec
|
||||
02 Track Mute
|
||||
03 Track Solo
|
||||
04 Any Solo
|
||||
05 Loop
|
||||
06 Punch
|
||||
|
||||
The size of the LCD is 20x2, and it is split into 10 cells, each cell
|
||||
being 4 characters wide. The cells progress across, then down. To write
|
||||
to the LCD, send the following sequence of bytes:
|
||||
|
||||
00 01 cc aa aa aa aa 00 (hexadecimal)
|
||||
|
||||
where:
|
||||
|
||||
cc - cell number
|
||||
aa - ASCII code
|
||||
|
||||
Here is a list of the cells to clarify:
|
||||
|
||||
00 row 0, column 0-3
|
||||
01 row 0, column 4-7
|
||||
02 row 0, column 8-11
|
||||
03 row 0, column 12-15
|
||||
04 row 0, column 16-19
|
||||
05 row 1, column 0-3
|
||||
06 row 1, column 4-7
|
||||
07 row 1, column 8-11
|
||||
08 row 1, column 12-15
|
||||
09 row 1, column 16-19
|
||||
|
||||
You should refer to the "Tranzport(tm) Native Mode Interface
|
||||
Description" document for a listing of the ASCII codes the LCD uses.
|
||||
|
||||
375
libs/surfaces/frontier/kernel_drivers/tests/tranzport.c
Normal file
375
libs/surfaces/frontier/kernel_drivers/tests/tranzport.c
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* tranzport 0.1 <tranzport.sf.net>
|
||||
* oct 18, 2005
|
||||
* arthur@artcmusic.com
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define VENDORID 0x165b
|
||||
#define PRODUCTID 0x8101
|
||||
|
||||
#define READ_ENDPOINT 0x81
|
||||
#define WRITE_ENDPOINT 0x02
|
||||
|
||||
enum {
|
||||
LIGHT_RECORD = 0,
|
||||
LIGHT_TRACKREC,
|
||||
LIGHT_TRACKMUTE,
|
||||
LIGHT_TRACKSOLO,
|
||||
LIGHT_ANYSOLO,
|
||||
LIGHT_LOOP,
|
||||
LIGHT_PUNCH
|
||||
};
|
||||
|
||||
#define BUTTONMASK_BATTERY 0x00004000
|
||||
#define BUTTONMASK_BACKLIGHT 0x00008000
|
||||
#define BUTTONMASK_TRACKLEFT 0x04000000
|
||||
#define BUTTONMASK_TRACKRIGHT 0x40000000
|
||||
#define BUTTONMASK_TRACKREC 0x00040000
|
||||
#define BUTTONMASK_TRACKMUTE 0x00400000
|
||||
#define BUTTONMASK_TRACKSOLO 0x00000400
|
||||
#define BUTTONMASK_UNDO 0x80000000
|
||||
#define BUTTONMASK_IN 0x02000000
|
||||
#define BUTTONMASK_OUT 0x20000000
|
||||
#define BUTTONMASK_PUNCH 0x00800000
|
||||
#define BUTTONMASK_LOOP 0x00080000
|
||||
#define BUTTONMASK_PREV 0x00020000
|
||||
#define BUTTONMASK_ADD 0x00200000
|
||||
#define BUTTONMASK_NEXT 0x00000200
|
||||
#define BUTTONMASK_REWIND 0x01000000
|
||||
#define BUTTONMASK_FASTFORWARD 0x10000000
|
||||
#define BUTTONMASK_STOP 0x00010000
|
||||
#define BUTTONMASK_PLAY 0x00100000
|
||||
#define BUTTONMASK_RECORD 0x00000100
|
||||
#define BUTTONMASK_SHIFT 0x08000000
|
||||
|
||||
#define STATUS_OFFLINE 0xff
|
||||
#define STATUS_ONLINE 0x01
|
||||
#define STATUS_OK 0x00
|
||||
|
||||
struct tranzport_s {
|
||||
int *dev;
|
||||
int udev;
|
||||
};
|
||||
|
||||
typedef struct tranzport_s tranzport_t;
|
||||
|
||||
void log_entry(FILE *fp, char *format, va_list ap)
|
||||
{
|
||||
vfprintf(fp, format, ap);
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
void log_error(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
log_entry(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vlog_error(char *format, va_list ap)
|
||||
{
|
||||
log_entry(stderr, format, ap);
|
||||
}
|
||||
|
||||
void die(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vlog_error(format, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport_core()
|
||||
{
|
||||
tranzport_t *z;
|
||||
int val;
|
||||
|
||||
z = malloc(sizeof(tranzport_t));
|
||||
if (!z)
|
||||
die("not enough memory");
|
||||
memset(z, 0, sizeof(tranzport_t));
|
||||
|
||||
z->udev = open("/dev/tranzport0",O_RDWR);
|
||||
if (z->udev < 1)
|
||||
die("unable to open tranzport");
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport()
|
||||
{
|
||||
return open_tranzport_core();
|
||||
}
|
||||
|
||||
void close_tranzport(tranzport_t *z)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = close(z->udev);
|
||||
if (val < 0)
|
||||
log_error("unable to release tranzport");
|
||||
|
||||
free(z);
|
||||
}
|
||||
|
||||
int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
|
||||
{
|
||||
int val;
|
||||
val = write(z->udev, cmd, 8);
|
||||
if (val < 0)
|
||||
return val;
|
||||
if (val != 8)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
if (cell > 9) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x01;
|
||||
cmd[2] = cell;
|
||||
cmd[3] = text[0];
|
||||
cmd[4] = text[1];
|
||||
cmd[5] = text[2];
|
||||
cmd[6] = text[3];
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, cmd, timeout);
|
||||
}
|
||||
|
||||
int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x01;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, &cmd[0], timeout);
|
||||
}
|
||||
|
||||
int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x00;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, &cmd[0], timeout);
|
||||
}
|
||||
|
||||
int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
int val;
|
||||
|
||||
memset(buf, 0xff, 8);
|
||||
val = read(z->udev, buf, 8);
|
||||
if (val < 0) {
|
||||
printf("errno: %d\n",errno);
|
||||
switch(errno) {
|
||||
case ENOENT: ;
|
||||
case ECONNRESET: ;
|
||||
case ESHUTDOWN: printf("dying\n"); exit(1); break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
if (val != 8)
|
||||
return -1;
|
||||
|
||||
/*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
|
||||
|
||||
*status = buf[1];
|
||||
|
||||
*buttons = 0;
|
||||
*buttons |= buf[2] << 24;
|
||||
*buttons |= buf[3] << 16;
|
||||
*buttons |= buf[4] << 8;
|
||||
*buttons |= buf[5];
|
||||
|
||||
*datawheel = buf[6];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
|
||||
{
|
||||
if (buttons & buttonmask) {
|
||||
if (buttons & BUTTONMASK_SHIFT) {
|
||||
tranzport_lightoff(z, light, 10);
|
||||
} else {
|
||||
tranzport_lighton(z, light, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_lights(tranzport_t *z, uint32_t buttons)
|
||||
{
|
||||
lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
|
||||
lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
|
||||
}
|
||||
|
||||
void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
|
||||
{
|
||||
if (buttons & buttonmask)
|
||||
printf(" %s", str);
|
||||
}
|
||||
|
||||
void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
|
||||
{
|
||||
printf("buttons: %x ", buttons);
|
||||
buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
|
||||
buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
|
||||
buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
|
||||
buttons_core(z, buttons, BUTTONMASK_IN, "in");
|
||||
buttons_core(z, buttons, BUTTONMASK_OUT, "out");
|
||||
buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
|
||||
buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
|
||||
buttons_core(z, buttons, BUTTONMASK_ADD, "add");
|
||||
buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
|
||||
buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
|
||||
buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
|
||||
buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
|
||||
buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
|
||||
buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
|
||||
if (datawheel)
|
||||
printf(" datawheel=%02x", datawheel);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void do_lcd(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, " ", 10);
|
||||
tranzport_lcdwrite(z, 1, "DISL", 10);
|
||||
tranzport_lcdwrite(z, 2, "EXIA", 10);
|
||||
tranzport_lcdwrite(z, 3, " FOR", 10);
|
||||
tranzport_lcdwrite(z, 4, " ", 10);
|
||||
|
||||
tranzport_lcdwrite(z, 5, " ", 10);
|
||||
tranzport_lcdwrite(z, 6, " CUR", 10);
|
||||
tranzport_lcdwrite(z, 7, "E FO", 10);
|
||||
tranzport_lcdwrite(z, 8, "UND ", 10);
|
||||
tranzport_lcdwrite(z, 9, " ", 10);
|
||||
}
|
||||
|
||||
void do_lcd2(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, "THE ", 10);
|
||||
tranzport_lcdwrite(z, 1, "TRAN", 10);
|
||||
tranzport_lcdwrite(z, 2, "ZPOR", 10);
|
||||
tranzport_lcdwrite(z, 3, "T RO", 10);
|
||||
tranzport_lcdwrite(z, 4, " KS", 10);
|
||||
|
||||
tranzport_lcdwrite(z, 5, "AWES", 10);
|
||||
tranzport_lcdwrite(z, 6, "OMEE", 10);
|
||||
tranzport_lcdwrite(z, 7, "LEEE", 10);
|
||||
tranzport_lcdwrite(z, 8, "UND ", 10);
|
||||
tranzport_lcdwrite(z, 9, "GROK", 10);
|
||||
}
|
||||
|
||||
int lights_off(tranzport_t *z) {
|
||||
static int i = 0;
|
||||
int j = 0;
|
||||
for(;j<2; j++,i = (i+1) % 7) {
|
||||
tranzport_lightoff(z, i, 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lights_on(tranzport_t *z) {
|
||||
static int i = 0;
|
||||
int j = 0;
|
||||
for(;j<2; j++,i = (i+1) % 7) {
|
||||
tranzport_lighton(z, i, 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
tranzport_t *z;
|
||||
uint8_t status;
|
||||
uint32_t buttons;
|
||||
uint8_t datawheel;
|
||||
int val;
|
||||
|
||||
z = open_tranzport();
|
||||
|
||||
do_lcd(z);
|
||||
|
||||
for(;;) {
|
||||
|
||||
// do_lcd(z);
|
||||
lights_on(z);
|
||||
// do_lcd2(z);
|
||||
|
||||
val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
|
||||
if (val < 0)
|
||||
continue;
|
||||
|
||||
if (status == STATUS_OFFLINE) {
|
||||
printf("offline: ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == STATUS_ONLINE) {
|
||||
printf("online: ");
|
||||
do_lcd(z);
|
||||
}
|
||||
|
||||
if (status == STATUS_OK) {
|
||||
printf("OK: ");
|
||||
do_lcd(z);
|
||||
}
|
||||
|
||||
// do_lights(z, buttons);
|
||||
do_buttons(z, buttons, datawheel);
|
||||
lights_off(z);
|
||||
}
|
||||
|
||||
close_tranzport(z);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
361
libs/surfaces/frontier/kernel_drivers/tests/tranzport_lights.c
Normal file
361
libs/surfaces/frontier/kernel_drivers/tests/tranzport_lights.c
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* tranzport 0.1 <tranzport.sf.net>
|
||||
* oct 18, 2005
|
||||
* arthur@artcmusic.com
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define VENDORID 0x165b
|
||||
#define PRODUCTID 0x8101
|
||||
|
||||
#define READ_ENDPOINT 0x81
|
||||
#define WRITE_ENDPOINT 0x02
|
||||
|
||||
enum {
|
||||
LIGHT_RECORD = 0,
|
||||
LIGHT_TRACKREC,
|
||||
LIGHT_TRACKMUTE,
|
||||
LIGHT_TRACKSOLO,
|
||||
LIGHT_ANYSOLO,
|
||||
LIGHT_LOOP,
|
||||
LIGHT_PUNCH
|
||||
};
|
||||
|
||||
#define BUTTONMASK_BATTERY 0x00004000
|
||||
#define BUTTONMASK_BACKLIGHT 0x00008000
|
||||
#define BUTTONMASK_TRACKLEFT 0x04000000
|
||||
#define BUTTONMASK_TRACKRIGHT 0x40000000
|
||||
#define BUTTONMASK_TRACKREC 0x00040000
|
||||
#define BUTTONMASK_TRACKMUTE 0x00400000
|
||||
#define BUTTONMASK_TRACKSOLO 0x00000400
|
||||
#define BUTTONMASK_UNDO 0x80000000
|
||||
#define BUTTONMASK_IN 0x02000000
|
||||
#define BUTTONMASK_OUT 0x20000000
|
||||
#define BUTTONMASK_PUNCH 0x00800000
|
||||
#define BUTTONMASK_LOOP 0x00080000
|
||||
#define BUTTONMASK_PREV 0x00020000
|
||||
#define BUTTONMASK_ADD 0x00200000
|
||||
#define BUTTONMASK_NEXT 0x00000200
|
||||
#define BUTTONMASK_REWIND 0x01000000
|
||||
#define BUTTONMASK_FASTFORWARD 0x10000000
|
||||
#define BUTTONMASK_STOP 0x00010000
|
||||
#define BUTTONMASK_PLAY 0x00100000
|
||||
#define BUTTONMASK_RECORD 0x00000100
|
||||
#define BUTTONMASK_SHIFT 0x08000000
|
||||
|
||||
#define STATUS_OFFLINE 0xff
|
||||
#define STATUS_ONLINE 0x01
|
||||
#define STATUS_OK 0x00
|
||||
|
||||
struct tranzport_s {
|
||||
int *dev;
|
||||
int udev;
|
||||
};
|
||||
|
||||
typedef struct tranzport_s tranzport_t;
|
||||
|
||||
void log_entry(FILE *fp, char *format, va_list ap)
|
||||
{
|
||||
vfprintf(fp, format, ap);
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
void log_error(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
log_entry(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vlog_error(char *format, va_list ap)
|
||||
{
|
||||
log_entry(stderr, format, ap);
|
||||
}
|
||||
|
||||
void die(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vlog_error(format, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport_core()
|
||||
{
|
||||
tranzport_t *z;
|
||||
int val;
|
||||
|
||||
z = malloc(sizeof(tranzport_t));
|
||||
if (!z)
|
||||
die("not enough memory");
|
||||
memset(z, 0, sizeof(tranzport_t));
|
||||
|
||||
z->udev = open("/dev/tranzport0",O_RDWR);
|
||||
if (!z->udev)
|
||||
die("unable to open tranzport");
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport()
|
||||
{
|
||||
return open_tranzport_core();
|
||||
}
|
||||
|
||||
void close_tranzport(tranzport_t *z)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = close(z->udev);
|
||||
if (val < 0)
|
||||
log_error("unable to release tranzport");
|
||||
|
||||
free(z);
|
||||
}
|
||||
|
||||
int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
|
||||
{
|
||||
int val;
|
||||
val = write(z->udev, cmd, 8);
|
||||
if (val < 0)
|
||||
return val;
|
||||
if (val != 8)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
if (cell > 9) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x01;
|
||||
cmd[2] = cell;
|
||||
cmd[3] = text[0];
|
||||
cmd[4] = text[1];
|
||||
cmd[5] = text[2];
|
||||
cmd[6] = text[3];
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, cmd, timeout);
|
||||
}
|
||||
|
||||
int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x01;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, &cmd[0], timeout);
|
||||
}
|
||||
|
||||
int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x00;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, &cmd[0], timeout);
|
||||
}
|
||||
|
||||
int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
int val;
|
||||
|
||||
memset(buf, 0xff, 8);
|
||||
val = read(z->udev, buf, 8);
|
||||
if (val < 0) {
|
||||
// printf("errno: %d\n",errno);
|
||||
return val;
|
||||
}
|
||||
if (val != 8)
|
||||
return -1;
|
||||
|
||||
/*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
|
||||
|
||||
*status = buf[1];
|
||||
|
||||
*buttons = 0;
|
||||
*buttons |= buf[2] << 24;
|
||||
*buttons |= buf[3] << 16;
|
||||
*buttons |= buf[4] << 8;
|
||||
*buttons |= buf[5];
|
||||
|
||||
*datawheel = buf[6];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
|
||||
{
|
||||
if (buttons & buttonmask) {
|
||||
if (buttons & BUTTONMASK_SHIFT) {
|
||||
tranzport_lightoff(z, light, 1000);
|
||||
} else {
|
||||
tranzport_lighton(z, light, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_lights(tranzport_t *z, uint32_t buttons)
|
||||
{
|
||||
lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
|
||||
lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
|
||||
}
|
||||
|
||||
void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
|
||||
{
|
||||
if (buttons & buttonmask)
|
||||
printf(" %s", str);
|
||||
}
|
||||
|
||||
void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
|
||||
{
|
||||
printf("buttons: %x ", buttons);
|
||||
buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
|
||||
buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
|
||||
buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
|
||||
buttons_core(z, buttons, BUTTONMASK_IN, "in");
|
||||
buttons_core(z, buttons, BUTTONMASK_OUT, "out");
|
||||
buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
|
||||
buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
|
||||
buttons_core(z, buttons, BUTTONMASK_ADD, "add");
|
||||
buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
|
||||
buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
|
||||
buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
|
||||
buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
|
||||
buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
|
||||
buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
|
||||
if (datawheel)
|
||||
printf(" datawheel=%02x", datawheel);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void do_lcd(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, " ", 1000);
|
||||
tranzport_lcdwrite(z, 1, "DISL", 1000);
|
||||
tranzport_lcdwrite(z, 2, "EXIA", 1000);
|
||||
tranzport_lcdwrite(z, 3, " FOR", 1000);
|
||||
tranzport_lcdwrite(z, 4, " ", 1000);
|
||||
|
||||
tranzport_lcdwrite(z, 5, " ", 1000);
|
||||
tranzport_lcdwrite(z, 6, " CUR", 1000);
|
||||
tranzport_lcdwrite(z, 7, "E FO", 1000);
|
||||
tranzport_lcdwrite(z, 8, "UND ", 1000);
|
||||
tranzport_lcdwrite(z, 9, " ", 1000);
|
||||
}
|
||||
|
||||
void do_lcd2(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, "THE ", 1000);
|
||||
tranzport_lcdwrite(z, 1, "TRAN", 1000);
|
||||
tranzport_lcdwrite(z, 2, "ZPOR", 1000);
|
||||
tranzport_lcdwrite(z, 3, "T RO", 1000);
|
||||
tranzport_lcdwrite(z, 4, "KS ", 1000);
|
||||
|
||||
tranzport_lcdwrite(z, 5, "AWES", 1000);
|
||||
tranzport_lcdwrite(z, 6, "OMEE", 1000);
|
||||
tranzport_lcdwrite(z, 7, "LEEE", 1000);
|
||||
tranzport_lcdwrite(z, 8, "WITH", 1000);
|
||||
tranzport_lcdwrite(z, 9, "ARDO", 1000);
|
||||
}
|
||||
|
||||
lights_off(tranzport_t *z) {
|
||||
int i;
|
||||
for(i=0;i<7;i++) {
|
||||
tranzport_lightoff(z, i, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
lights_on(tranzport_t *z) {
|
||||
int i;
|
||||
for(i=0;i<7;i++) {
|
||||
tranzport_lighton(z, i, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
tranzport_t *z;
|
||||
uint8_t status;
|
||||
uint32_t buttons;
|
||||
uint8_t datawheel;
|
||||
int val;
|
||||
|
||||
z = open_tranzport();
|
||||
|
||||
do_lcd(z);
|
||||
|
||||
for(;;) {
|
||||
|
||||
do_lcd(z);
|
||||
lights_on(z);
|
||||
do_lcd2(z);
|
||||
lights_off(z);
|
||||
|
||||
// val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
|
||||
val = -1;
|
||||
if (val < 0)
|
||||
continue;
|
||||
|
||||
if (status == STATUS_OFFLINE) {
|
||||
printf("offline: ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == STATUS_ONLINE) {
|
||||
printf("online: ");
|
||||
do_lcd(z);
|
||||
}
|
||||
|
||||
do_lights(z, buttons);
|
||||
do_buttons(z, buttons, datawheel);
|
||||
}
|
||||
|
||||
close_tranzport(z);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
27
libs/surfaces/frontier/kernel_drivers/tests/tranzport_tests.sh
Executable file
27
libs/surfaces/frontier/kernel_drivers/tests/tranzport_tests.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "Testing lights"
|
||||
tranzport_lights &
|
||||
A=$!
|
||||
sleep 30
|
||||
kill $A
|
||||
echo "Testing interleaved_reads/writes"
|
||||
tranzport &
|
||||
A=$!
|
||||
sleep 30
|
||||
kill $A
|
||||
|
||||
exit 0
|
||||
|
||||
# not done yet
|
||||
echo "Testing_screen"
|
||||
tranzport_screen &
|
||||
A=$!
|
||||
sleep 30
|
||||
kill $A
|
||||
echo "Testing_reads"
|
||||
tranzport_read &
|
||||
A=$!
|
||||
sleep 30
|
||||
kill $A
|
||||
|
||||
1065
libs/surfaces/frontier/kernel_drivers/tranzport.c
Normal file
1065
libs/surfaces/frontier/kernel_drivers/tranzport.c
Normal file
File diff suppressed because it is too large
Load diff
17
libs/surfaces/frontier/tests/Makefile
Normal file
17
libs/surfaces/frontier/tests/Makefile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Some basic utilities for testing the tranzport's I/O
|
||||
# eventually "tranzport" will become a flexible command
|
||||
#
|
||||
#
|
||||
all: tranzport tranzport_lights
|
||||
|
||||
tranzport: tranzport.c
|
||||
gcc -g -Wall -o tranzport tranzport.c
|
||||
|
||||
tranzport_lights: tranzport_lights.c
|
||||
gcc -g -Wall -o tranzport_lights tranzport_lights.c
|
||||
|
||||
clean::
|
||||
rm -f core .*.cmd *.o *.ko *.mod.c Module.symvers *.bak .\#* *~
|
||||
rm -rf .tmp_versions tranzport tranzport_lights
|
||||
|
||||
|
||||
104
libs/surfaces/frontier/tests/README
Normal file
104
libs/surfaces/frontier/tests/README
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
tranzport 0.1 <tranzport.sf.net>
|
||||
oct 18, 2005
|
||||
arthur@artcmusic.com
|
||||
---
|
||||
|
||||
The Frontier Design Tranzport(tm) (www.frontierdesign.com) is a simple
|
||||
wireless USB device. It is not a MIDI device. The document on their web
|
||||
site "Tranzport(tm) Native Mode Interface Description" describes the
|
||||
Tranzport(tm) as if it were a MIDI device, but this is implemented by their
|
||||
Windows and Macintosh software drivers.
|
||||
|
||||
This code will allow you to use your Tranzport(tm) at a lower level of
|
||||
abstraction. This code relies on libusb, which can be obtained from
|
||||
libusb.sourceforge.net.
|
||||
|
||||
To compile the program, type "make". You should end up with a executable
|
||||
called "tranzport". You'll probably have to run this program as root.
|
||||
|
||||
Using the program is straightforward. It will simply tell you which
|
||||
buttons are being pressed and what not. If you press one of the buttons
|
||||
with a light, the light will turn on. If you hold shift and press one of
|
||||
the buttons with a light, the light will turn off. If you take out the
|
||||
batteries to the device (or go out of range), it will tell you that the
|
||||
device is offline. When you replace the batteries (or come back in
|
||||
range), it should tell you it is back online.
|
||||
|
||||
Once you understand how everything works, you should be able to
|
||||
incorporate it into your own setup however you wish.
|
||||
|
||||
This code was developed on a Linux machine, but (theoretically) it
|
||||
should work on any system that is supported by libusb, since that is how
|
||||
it communicates with the device.
|
||||
|
||||
Here are a few more details about the device:
|
||||
|
||||
There are two endpoints for communication with the device. All data
|
||||
reads and writes are done in 8-byte segments.
|
||||
|
||||
One endpoint is for interrupt reads. This is used to read button data
|
||||
from the device. It also supplies status information for when the device
|
||||
goes out of range and comes back in range, loses power and regains
|
||||
power, etc. The format of the data is:
|
||||
|
||||
00 ss bb bb bb bb dd 00 (hexadecimal)
|
||||
|
||||
where:
|
||||
|
||||
ss - status code, 01=online ff=offline
|
||||
bb - button bits
|
||||
dd - data wheel, 01-3f=forward 41-7f=backward
|
||||
|
||||
Please refer to the source code for a list of the button bits.
|
||||
|
||||
The other endpoint is for interrupt writes. This is used to toggle the
|
||||
lights on the device, and to write data to the LCD.
|
||||
|
||||
There are 7 lights on the device. To turn a light on, send the following
|
||||
sequence of bytes:
|
||||
|
||||
00 00 nn 01 00 00 00 00 (hexadecimal)
|
||||
|
||||
where nn is the light number.
|
||||
|
||||
To turn a light off:
|
||||
|
||||
00 00 nn 00 00 00 00 00 (hexadecimal)
|
||||
|
||||
Here is the list of lights:
|
||||
|
||||
00 Record
|
||||
01 Track Rec
|
||||
02 Track Mute
|
||||
03 Track Solo
|
||||
04 Any Solo
|
||||
05 Loop
|
||||
06 Punch
|
||||
|
||||
The size of the LCD is 20x2, and it is split into 10 cells, each cell
|
||||
being 4 characters wide. The cells progress across, then down. To write
|
||||
to the LCD, send the following sequence of bytes:
|
||||
|
||||
00 01 cc aa aa aa aa 00 (hexadecimal)
|
||||
|
||||
where:
|
||||
|
||||
cc - cell number
|
||||
aa - ASCII code
|
||||
|
||||
Here is a list of the cells to clarify:
|
||||
|
||||
00 row 0, column 0-3
|
||||
01 row 0, column 4-7
|
||||
02 row 0, column 8-11
|
||||
03 row 0, column 12-15
|
||||
04 row 0, column 16-19
|
||||
05 row 1, column 0-3
|
||||
06 row 1, column 4-7
|
||||
07 row 1, column 8-11
|
||||
08 row 1, column 12-15
|
||||
09 row 1, column 16-19
|
||||
|
||||
You should refer to the "Tranzport(tm) Native Mode Interface
|
||||
Description" document for a listing of the ASCII codes the LCD uses.
|
||||
|
||||
347
libs/surfaces/frontier/tests/tranzport.c
Normal file
347
libs/surfaces/frontier/tests/tranzport.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* tranzport 0.1 <tranzport.sf.net>
|
||||
* oct 18, 2005
|
||||
* arthur@artcmusic.com
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usb.h>
|
||||
|
||||
#define VENDORID 0x165b
|
||||
#define PRODUCTID 0x8101
|
||||
|
||||
#define READ_ENDPOINT 0x81
|
||||
#define WRITE_ENDPOINT 0x02
|
||||
|
||||
enum {
|
||||
LIGHT_RECORD = 0,
|
||||
LIGHT_TRACKREC,
|
||||
LIGHT_TRACKMUTE,
|
||||
LIGHT_TRACKSOLO,
|
||||
LIGHT_ANYSOLO,
|
||||
LIGHT_LOOP,
|
||||
LIGHT_PUNCH
|
||||
};
|
||||
|
||||
#define BUTTONMASK_BATTERY 0x00004000
|
||||
#define BUTTONMASK_BACKLIGHT 0x00008000
|
||||
#define BUTTONMASK_TRACKLEFT 0x04000000
|
||||
#define BUTTONMASK_TRACKRIGHT 0x40000000
|
||||
#define BUTTONMASK_TRACKREC 0x00040000
|
||||
#define BUTTONMASK_TRACKMUTE 0x00400000
|
||||
#define BUTTONMASK_TRACKSOLO 0x00000400
|
||||
#define BUTTONMASK_UNDO 0x80000000
|
||||
#define BUTTONMASK_IN 0x02000000
|
||||
#define BUTTONMASK_OUT 0x20000000
|
||||
#define BUTTONMASK_PUNCH 0x00800000
|
||||
#define BUTTONMASK_LOOP 0x00080000
|
||||
#define BUTTONMASK_PREV 0x00020000
|
||||
#define BUTTONMASK_ADD 0x00200000
|
||||
#define BUTTONMASK_NEXT 0x00000200
|
||||
#define BUTTONMASK_REWIND 0x01000000
|
||||
#define BUTTONMASK_FASTFORWARD 0x10000000
|
||||
#define BUTTONMASK_STOP 0x00010000
|
||||
#define BUTTONMASK_PLAY 0x00100000
|
||||
#define BUTTONMASK_RECORD 0x00000100
|
||||
#define BUTTONMASK_SHIFT 0x08000000
|
||||
|
||||
#define STATUS_OFFLINE 0xff
|
||||
#define STATUS_ONLINE 0x01
|
||||
|
||||
struct tranzport_s {
|
||||
struct usb_device *dev;
|
||||
usb_dev_handle *udev;
|
||||
};
|
||||
|
||||
typedef struct tranzport_s tranzport_t;
|
||||
|
||||
void log_entry(FILE *fp, char *format, va_list ap)
|
||||
{
|
||||
vfprintf(fp, format, ap);
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
void log_error(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
log_entry(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vlog_error(char *format, va_list ap)
|
||||
{
|
||||
log_entry(stderr, format, ap);
|
||||
}
|
||||
|
||||
void die(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vlog_error(format, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport_core(struct usb_device *dev)
|
||||
{
|
||||
tranzport_t *z;
|
||||
int val;
|
||||
|
||||
z = malloc(sizeof(tranzport_t));
|
||||
if (!z)
|
||||
die("not enough memory");
|
||||
memset(z, 0, sizeof(tranzport_t));
|
||||
|
||||
z->dev = dev;
|
||||
z->udev = usb_open(z->dev);
|
||||
if (!z->udev)
|
||||
die("unable to open tranzport");
|
||||
|
||||
val = usb_claim_interface(z->udev, 0);
|
||||
if (val < 0)
|
||||
die("unable to claim tranzport");
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport()
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct usb_device *dev;
|
||||
|
||||
usb_init();
|
||||
usb_find_busses();
|
||||
usb_find_devices();
|
||||
|
||||
for(bus=usb_busses; bus; bus=bus->next) {
|
||||
for(dev=bus->devices; dev; dev=dev->next) {
|
||||
if (dev->descriptor.idVendor != VENDORID)
|
||||
continue;
|
||||
if (dev->descriptor.idProduct != PRODUCTID)
|
||||
continue;
|
||||
|
||||
return open_tranzport_core(dev);
|
||||
}
|
||||
}
|
||||
|
||||
die("can't find tranzport");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close_tranzport(tranzport_t *z)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = usb_release_interface(z->udev, 0);
|
||||
if (val < 0)
|
||||
log_error("unable to release tranzport");
|
||||
|
||||
val = usb_close(z->udev);
|
||||
if (val < 0)
|
||||
log_error("unable to close tranzport");
|
||||
|
||||
free(z);
|
||||
}
|
||||
|
||||
int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
|
||||
{
|
||||
int val;
|
||||
val = usb_interrupt_write(z->udev, WRITE_ENDPOINT, cmd, 8, timeout);
|
||||
if (val < 0)
|
||||
return val;
|
||||
if (val != 8)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
if (cell > 9) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x01;
|
||||
cmd[2] = cell;
|
||||
cmd[3] = text[0];
|
||||
cmd[4] = text[1];
|
||||
cmd[5] = text[2];
|
||||
cmd[6] = text[3];
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, cmd, timeout);
|
||||
}
|
||||
|
||||
int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x01;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, cmd, timeout);
|
||||
}
|
||||
|
||||
int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x00;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, cmd, timeout);
|
||||
}
|
||||
|
||||
int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
int val;
|
||||
|
||||
memset(buf, 0, 8);
|
||||
val = usb_interrupt_read(z->udev, READ_ENDPOINT, buf, 8, timeout);
|
||||
if (val < 0)
|
||||
return val;
|
||||
if (val != 8)
|
||||
return -1;
|
||||
|
||||
/*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
|
||||
|
||||
*status = buf[1];
|
||||
|
||||
*buttons = 0;
|
||||
*buttons |= buf[2] << 24;
|
||||
*buttons |= buf[3] << 16;
|
||||
*buttons |= buf[4] << 8;
|
||||
*buttons |= buf[5];
|
||||
|
||||
*datawheel = buf[6];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
|
||||
{
|
||||
if (buttons & buttonmask) {
|
||||
if (buttons & BUTTONMASK_SHIFT) {
|
||||
tranzport_lightoff(z, light, 1000);
|
||||
} else {
|
||||
tranzport_lighton(z, light, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_lights(tranzport_t *z, uint32_t buttons)
|
||||
{
|
||||
lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
|
||||
lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
|
||||
}
|
||||
|
||||
void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
|
||||
{
|
||||
if (buttons & buttonmask)
|
||||
printf(" %s", str);
|
||||
}
|
||||
|
||||
void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
|
||||
{
|
||||
printf("buttons:");
|
||||
buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
|
||||
buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
|
||||
buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
|
||||
buttons_core(z, buttons, BUTTONMASK_IN, "in");
|
||||
buttons_core(z, buttons, BUTTONMASK_OUT, "out");
|
||||
buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
|
||||
buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
|
||||
buttons_core(z, buttons, BUTTONMASK_ADD, "add");
|
||||
buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
|
||||
buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
|
||||
buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
|
||||
buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
|
||||
buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
|
||||
buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
|
||||
if (datawheel)
|
||||
printf(" datawheel=%02x", datawheel);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void do_lcd(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, " ", 1000);
|
||||
tranzport_lcdwrite(z, 1, "DISL", 1000);
|
||||
tranzport_lcdwrite(z, 2, "EXIA", 1000);
|
||||
tranzport_lcdwrite(z, 3, " FOR", 1000);
|
||||
tranzport_lcdwrite(z, 4, " ", 1000);
|
||||
|
||||
tranzport_lcdwrite(z, 5, " ", 1000);
|
||||
tranzport_lcdwrite(z, 6, " CUR", 1000);
|
||||
tranzport_lcdwrite(z, 7, "E FO", 1000);
|
||||
tranzport_lcdwrite(z, 8, "UND ", 1000);
|
||||
tranzport_lcdwrite(z, 9, " ", 1000);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
tranzport_t *z;
|
||||
uint8_t status;
|
||||
uint32_t buttons;
|
||||
uint8_t datawheel;
|
||||
int val;
|
||||
|
||||
z = open_tranzport();
|
||||
|
||||
do_lcd(z);
|
||||
|
||||
for(;;) {
|
||||
val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
|
||||
if (val < 0)
|
||||
continue;
|
||||
|
||||
if (status == STATUS_OFFLINE) {
|
||||
printf("offline\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == STATUS_ONLINE) {
|
||||
printf("online\n");
|
||||
do_lcd(z);
|
||||
}
|
||||
|
||||
do_lights(z, buttons);
|
||||
do_buttons(z, buttons, datawheel);
|
||||
}
|
||||
|
||||
close_tranzport(z);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
361
libs/surfaces/frontier/tests/tranzport_lights.c
Normal file
361
libs/surfaces/frontier/tests/tranzport_lights.c
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* tranzport 0.1 <tranzport.sf.net>
|
||||
* oct 18, 2005
|
||||
* arthur@artcmusic.com
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#define VENDORID 0x165b
|
||||
#define PRODUCTID 0x8101
|
||||
|
||||
#define READ_ENDPOINT 0x81
|
||||
#define WRITE_ENDPOINT 0x02
|
||||
|
||||
enum {
|
||||
LIGHT_RECORD = 0,
|
||||
LIGHT_TRACKREC,
|
||||
LIGHT_TRACKMUTE,
|
||||
LIGHT_TRACKSOLO,
|
||||
LIGHT_ANYSOLO,
|
||||
LIGHT_LOOP,
|
||||
LIGHT_PUNCH
|
||||
};
|
||||
|
||||
#define BUTTONMASK_BATTERY 0x00004000
|
||||
#define BUTTONMASK_BACKLIGHT 0x00008000
|
||||
#define BUTTONMASK_TRACKLEFT 0x04000000
|
||||
#define BUTTONMASK_TRACKRIGHT 0x40000000
|
||||
#define BUTTONMASK_TRACKREC 0x00040000
|
||||
#define BUTTONMASK_TRACKMUTE 0x00400000
|
||||
#define BUTTONMASK_TRACKSOLO 0x00000400
|
||||
#define BUTTONMASK_UNDO 0x80000000
|
||||
#define BUTTONMASK_IN 0x02000000
|
||||
#define BUTTONMASK_OUT 0x20000000
|
||||
#define BUTTONMASK_PUNCH 0x00800000
|
||||
#define BUTTONMASK_LOOP 0x00080000
|
||||
#define BUTTONMASK_PREV 0x00020000
|
||||
#define BUTTONMASK_ADD 0x00200000
|
||||
#define BUTTONMASK_NEXT 0x00000200
|
||||
#define BUTTONMASK_REWIND 0x01000000
|
||||
#define BUTTONMASK_FASTFORWARD 0x10000000
|
||||
#define BUTTONMASK_STOP 0x00010000
|
||||
#define BUTTONMASK_PLAY 0x00100000
|
||||
#define BUTTONMASK_RECORD 0x00000100
|
||||
#define BUTTONMASK_SHIFT 0x08000000
|
||||
|
||||
#define STATUS_OFFLINE 0xff
|
||||
#define STATUS_ONLINE 0x01
|
||||
#define STATUS_OK 0x00
|
||||
|
||||
struct tranzport_s {
|
||||
int *dev;
|
||||
int udev;
|
||||
};
|
||||
|
||||
typedef struct tranzport_s tranzport_t;
|
||||
|
||||
void log_entry(FILE *fp, char *format, va_list ap)
|
||||
{
|
||||
vfprintf(fp, format, ap);
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
void log_error(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
log_entry(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vlog_error(char *format, va_list ap)
|
||||
{
|
||||
log_entry(stderr, format, ap);
|
||||
}
|
||||
|
||||
void die(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vlog_error(format, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport_core()
|
||||
{
|
||||
tranzport_t *z;
|
||||
int val;
|
||||
|
||||
z = malloc(sizeof(tranzport_t));
|
||||
if (!z)
|
||||
die("not enough memory");
|
||||
memset(z, 0, sizeof(tranzport_t));
|
||||
|
||||
z->udev = open("/dev/tranzport0",O_RDWR);
|
||||
if (!z->udev)
|
||||
die("unable to open tranzport");
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
tranzport_t *open_tranzport()
|
||||
{
|
||||
return open_tranzport_core();
|
||||
}
|
||||
|
||||
void close_tranzport(tranzport_t *z)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = close(z->udev);
|
||||
if (val < 0)
|
||||
log_error("unable to release tranzport");
|
||||
|
||||
free(z);
|
||||
}
|
||||
|
||||
int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
|
||||
{
|
||||
int val;
|
||||
val = write(z->udev, cmd, 8);
|
||||
if (val < 0)
|
||||
return val;
|
||||
if (val != 8)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
if (cell > 9) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x01;
|
||||
cmd[2] = cell;
|
||||
cmd[3] = text[0];
|
||||
cmd[4] = text[1];
|
||||
cmd[5] = text[2];
|
||||
cmd[6] = text[3];
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, cmd, timeout);
|
||||
}
|
||||
|
||||
int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x01;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, &cmd[0], timeout);
|
||||
}
|
||||
|
||||
int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x00;
|
||||
cmd[2] = light;
|
||||
cmd[3] = 0x00;
|
||||
cmd[4] = 0x00;
|
||||
cmd[5] = 0x00;
|
||||
cmd[6] = 0x00;
|
||||
cmd[7] = 0x00;
|
||||
|
||||
return tranzport_write_core(z, &cmd[0], timeout);
|
||||
}
|
||||
|
||||
int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
int val;
|
||||
|
||||
memset(buf, 0xff, 8);
|
||||
val = read(z->udev, buf, 8);
|
||||
if (val < 0) {
|
||||
// printf("errno: %d\n",errno);
|
||||
return val;
|
||||
}
|
||||
if (val != 8)
|
||||
return -1;
|
||||
|
||||
/*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
|
||||
|
||||
*status = buf[1];
|
||||
|
||||
*buttons = 0;
|
||||
*buttons |= buf[2] << 24;
|
||||
*buttons |= buf[3] << 16;
|
||||
*buttons |= buf[4] << 8;
|
||||
*buttons |= buf[5];
|
||||
|
||||
*datawheel = buf[6];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
|
||||
{
|
||||
if (buttons & buttonmask) {
|
||||
if (buttons & BUTTONMASK_SHIFT) {
|
||||
tranzport_lightoff(z, light, 1000);
|
||||
} else {
|
||||
tranzport_lighton(z, light, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_lights(tranzport_t *z, uint32_t buttons)
|
||||
{
|
||||
lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
|
||||
lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
|
||||
lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
|
||||
}
|
||||
|
||||
void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
|
||||
{
|
||||
if (buttons & buttonmask)
|
||||
printf(" %s", str);
|
||||
}
|
||||
|
||||
void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
|
||||
{
|
||||
printf("buttons: %x ", buttons);
|
||||
buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
|
||||
buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
|
||||
buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
|
||||
buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
|
||||
buttons_core(z, buttons, BUTTONMASK_IN, "in");
|
||||
buttons_core(z, buttons, BUTTONMASK_OUT, "out");
|
||||
buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
|
||||
buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
|
||||
buttons_core(z, buttons, BUTTONMASK_ADD, "add");
|
||||
buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
|
||||
buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
|
||||
buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
|
||||
buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
|
||||
buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
|
||||
buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
|
||||
buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
|
||||
if (datawheel)
|
||||
printf(" datawheel=%02x", datawheel);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void do_lcd(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, " ", 1000);
|
||||
tranzport_lcdwrite(z, 1, "DISL", 1000);
|
||||
tranzport_lcdwrite(z, 2, "EXIA", 1000);
|
||||
tranzport_lcdwrite(z, 3, " FOR", 1000);
|
||||
tranzport_lcdwrite(z, 4, " ", 1000);
|
||||
|
||||
tranzport_lcdwrite(z, 5, " ", 1000);
|
||||
tranzport_lcdwrite(z, 6, " CUR", 1000);
|
||||
tranzport_lcdwrite(z, 7, "E FO", 1000);
|
||||
tranzport_lcdwrite(z, 8, "UND ", 1000);
|
||||
tranzport_lcdwrite(z, 9, " ", 1000);
|
||||
}
|
||||
|
||||
void do_lcd2(tranzport_t *z)
|
||||
{
|
||||
tranzport_lcdwrite(z, 0, "THE ", 1000);
|
||||
tranzport_lcdwrite(z, 1, "TRAN", 1000);
|
||||
tranzport_lcdwrite(z, 2, "ZPOR", 1000);
|
||||
tranzport_lcdwrite(z, 3, "T RO", 1000);
|
||||
tranzport_lcdwrite(z, 4, " KS", 1000);
|
||||
|
||||
tranzport_lcdwrite(z, 5, "AWES", 1000);
|
||||
tranzport_lcdwrite(z, 6, "OMEE", 1000);
|
||||
tranzport_lcdwrite(z, 7, "LEEE", 1000);
|
||||
tranzport_lcdwrite(z, 8, "UND ", 1000);
|
||||
tranzport_lcdwrite(z, 9, "GROK", 1000);
|
||||
}
|
||||
|
||||
lights_off(tranzport_t *z) {
|
||||
int i;
|
||||
for(i=0;i<7;i++) {
|
||||
tranzport_lightoff(z, i, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
lights_on(tranzport_t *z) {
|
||||
int i;
|
||||
for(i=0;i<7;i++) {
|
||||
tranzport_lighton(z, i, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
tranzport_t *z;
|
||||
uint8_t status;
|
||||
uint32_t buttons;
|
||||
uint8_t datawheel;
|
||||
int val;
|
||||
|
||||
z = open_tranzport();
|
||||
|
||||
do_lcd(z);
|
||||
|
||||
for(;;) {
|
||||
|
||||
do_lcd(z);
|
||||
lights_on(z);
|
||||
do_lcd2(z);
|
||||
lights_off(z);
|
||||
|
||||
// val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
|
||||
val = -1;
|
||||
if (val < 0)
|
||||
continue;
|
||||
|
||||
if (status == STATUS_OFFLINE) {
|
||||
printf("offline: ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == STATUS_ONLINE) {
|
||||
printf("online: ");
|
||||
do_lcd(z);
|
||||
}
|
||||
|
||||
do_lights(z, buttons);
|
||||
do_buttons(z, buttons, datawheel);
|
||||
}
|
||||
|
||||
close_tranzport(z);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
56
libs/surfaces/frontier/tranzport/SConscript
Normal file
56
libs/surfaces/frontier/tranzport/SConscript
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# -*- python -*-
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
Import('env final_prefix install_prefix final_config_prefix libraries i18n')
|
||||
|
||||
tranzport = env.Copy()
|
||||
|
||||
#
|
||||
# this defines the version number of libardour_tranzport
|
||||
#
|
||||
|
||||
domain = 'ardour_tranzport'
|
||||
|
||||
tranzport.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
|
||||
tranzport.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
|
||||
tranzport.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
|
||||
tranzport.Append(PACKAGE = domain)
|
||||
tranzport.Append(POTFILE = domain + '.pot')
|
||||
|
||||
tranzport_files=Split("""
|
||||
interface.cc
|
||||
tranzport_control_protocol.cc
|
||||
""")
|
||||
|
||||
tranzport.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
|
||||
tranzport.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
|
||||
tranzport.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
|
||||
tranzport.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
|
||||
|
||||
tranzport.Merge ([
|
||||
libraries['ardour'],
|
||||
libraries['ardour_cp'],
|
||||
libraries['sigc2'],
|
||||
libraries['pbd'],
|
||||
libraries['midi++2'],
|
||||
libraries['xml'],
|
||||
libraries['usb'],
|
||||
libraries['glib2'],
|
||||
libraries['glibmm2']
|
||||
])
|
||||
|
||||
libardour_tranzport = tranzport.SharedLibrary('ardour_tranzport', tranzport_files)
|
||||
|
||||
if tranzport['TRANZPORT']:
|
||||
Default(libardour_tranzport)
|
||||
if env['NLS']:
|
||||
i18n (tranzport, tranzport_files, env)
|
||||
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'surfaces'), libardour_tranzport))
|
||||
|
||||
env.Alias('tarball', env.Distribute (env['DISTTREE'],
|
||||
[ 'SConscript' ] +
|
||||
tranzport_files +
|
||||
glob.glob('po/*.po') + glob.glob('*.h')))
|
||||
51
libs/surfaces/frontier/tranzport/interface.cc
Normal file
51
libs/surfaces/frontier/tranzport/interface.cc
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include <control_protocol/control_protocol.h>
|
||||
#include "tranzport_control_protocol.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
ControlProtocol*
|
||||
new_tranzport_protocol (ControlProtocolDescriptor* descriptor, Session* s)
|
||||
{
|
||||
TranzportControlProtocol* tcp = new TranzportControlProtocol (*s);
|
||||
|
||||
if (tcp->set_active (true)) {
|
||||
delete tcp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tcp;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
delete_tranzport_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
|
||||
{
|
||||
delete cp;
|
||||
}
|
||||
|
||||
bool
|
||||
probe_tranzport_protocol (ControlProtocolDescriptor* descriptor)
|
||||
{
|
||||
return TranzportControlProtocol::probe();
|
||||
}
|
||||
|
||||
static ControlProtocolDescriptor tranzport_descriptor = {
|
||||
name : "Tranzport",
|
||||
id : "uri://ardour.org/surfaces/tranzport:0",
|
||||
ptr : 0,
|
||||
module : 0,
|
||||
mandatory : 0,
|
||||
supports_feedback : false,
|
||||
probe : probe_tranzport_protocol,
|
||||
initialize : new_tranzport_protocol,
|
||||
destroy : delete_tranzport_protocol
|
||||
};
|
||||
|
||||
|
||||
extern "C" {
|
||||
ControlProtocolDescriptor*
|
||||
protocol_descriptor () {
|
||||
return &tranzport_descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$Id$
|
||||
$Id: tranzport_control_protocol.cc 1252 2006-12-29 19:13:18Z sampo $
|
||||
*/
|
||||
|
||||
/* Design notes: The tranzport is a unique device, basically a
|
||||
|
|
@ -1562,7 +1562,7 @@ TranzportControlProtocol::button_event_record_release (bool shifted)
|
|||
|
||||
void button_event_mute (bool pressed, bool shifted)
|
||||
{
|
||||
//static int was_pressed = 0;
|
||||
static int was_pressed = 0;
|
||||
// if(pressed) { }
|
||||
}
|
||||
|
||||
320
libs/surfaces/frontier/tranzport/tranzport_control_protocol.h
Normal file
320
libs/surfaces/frontier/tranzport/tranzport_control_protocol.h
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
|
||||
#ifndef ardour_tranzport_control_protocol_h
|
||||
#define ardour_tranzport_control_protocol_h
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
|
||||
#include <control_protocol/control_protocol.h>
|
||||
|
||||
class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
||||
{
|
||||
public:
|
||||
TranzportControlProtocol (ARDOUR::Session&);
|
||||
virtual ~TranzportControlProtocol();
|
||||
|
||||
int set_active (bool yn);
|
||||
|
||||
static bool probe ();
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
private:
|
||||
static const int VENDORID = 0x165b;
|
||||
static const int PRODUCTID = 0x8101;
|
||||
static const int READ_ENDPOINT = 0x81;
|
||||
static const int WRITE_ENDPOINT = 0x02;
|
||||
const static int STATUS_OFFLINE = 0xff;
|
||||
const static int STATUS_ONLINE = 0x01;
|
||||
const static uint8_t WheelDirectionThreshold = 0x3f;
|
||||
|
||||
enum LightID {
|
||||
LightRecord = 0,
|
||||
LightTrackrec,
|
||||
LightTrackmute,
|
||||
LightTracksolo,
|
||||
LightAnysolo,
|
||||
LightLoop,
|
||||
LightPunch
|
||||
};
|
||||
|
||||
enum ButtonID {
|
||||
ButtonBattery = 0x00004000,
|
||||
ButtonBacklight = 0x00008000,
|
||||
ButtonTrackLeft = 0x04000000,
|
||||
ButtonTrackRight = 0x40000000,
|
||||
ButtonTrackRec = 0x00040000,
|
||||
ButtonTrackMute = 0x00400000,
|
||||
ButtonTrackSolo = 0x00000400,
|
||||
ButtonUndo = 0x80000000,
|
||||
ButtonIn = 0x02000000,
|
||||
ButtonOut = 0x20000000,
|
||||
ButtonPunch = 0x00800000,
|
||||
ButtonLoop = 0x00080000,
|
||||
ButtonPrev = 0x00020000,
|
||||
ButtonAdd = 0x00200000,
|
||||
ButtonNext = 0x00000200,
|
||||
ButtonRewind = 0x01000000,
|
||||
ButtonFastForward = 0x10000000,
|
||||
ButtonStop = 0x00010000,
|
||||
ButtonPlay = 0x00100000,
|
||||
ButtonRecord = 0x00000100,
|
||||
ButtonShift = 0x08000000
|
||||
};
|
||||
|
||||
enum WheelShiftMode {
|
||||
WheelShiftGain,
|
||||
WheelShiftPan,
|
||||
WheelShiftMaster,
|
||||
WheelShiftMarker
|
||||
};
|
||||
|
||||
enum WheelMode {
|
||||
WheelTimeline,
|
||||
WheelScrub,
|
||||
WheelShuttle
|
||||
};
|
||||
|
||||
// FIXME - look at gtk2_ardour for snap settings
|
||||
|
||||
enum WheelIncrement {
|
||||
WheelIncrSlave,
|
||||
WheelIncrScreen,
|
||||
WheelIncrSample,
|
||||
WheelIncrBeat,
|
||||
WheelIncrBar,
|
||||
WheelIncrSecond,
|
||||
WheelIncrMinute
|
||||
};
|
||||
|
||||
enum DisplayMode {
|
||||
DisplayNormal,
|
||||
DisplayRecording,
|
||||
DisplayRecordingMeter,
|
||||
DisplayBigMeter,
|
||||
DisplayConfig,
|
||||
DisplayBling,
|
||||
DisplayBlingMeter
|
||||
};
|
||||
|
||||
enum BlingMode {
|
||||
BlingOff,
|
||||
BlingKit,
|
||||
BlingRotating,
|
||||
BlingPairs,
|
||||
BlingRows,
|
||||
BlingFlashAll
|
||||
};
|
||||
|
||||
pthread_t thread;
|
||||
uint32_t buttonmask;
|
||||
uint32_t timeout;
|
||||
uint32_t inflight;
|
||||
uint8_t _datawheel;
|
||||
uint8_t _device_status;
|
||||
uint32_t current_track_id;
|
||||
WheelMode wheel_mode;
|
||||
WheelShiftMode wheel_shift_mode;
|
||||
DisplayMode display_mode;
|
||||
BlingMode bling_mode;
|
||||
WheelIncrement wheel_increment;
|
||||
usb_dev_handle* udev;
|
||||
|
||||
ARDOUR::gain_t gain_fraction;
|
||||
|
||||
Glib::Mutex update_lock;
|
||||
|
||||
bool screen_invalid[2][20];
|
||||
char screen_current[2][20];
|
||||
char screen_pending[2][20];
|
||||
char screen_flash[2][20];
|
||||
|
||||
bool lights_invalid[7];
|
||||
bool lights_current[7];
|
||||
bool lights_pending[7];
|
||||
bool lights_flash[7];
|
||||
|
||||
uint32_t last_bars;
|
||||
uint32_t last_beats;
|
||||
uint32_t last_ticks;
|
||||
|
||||
bool last_negative;
|
||||
uint32_t last_hrs;
|
||||
uint32_t last_mins;
|
||||
uint32_t last_secs;
|
||||
uint32_t last_frames;
|
||||
nframes_t last_where;
|
||||
ARDOUR::gain_t last_track_gain;
|
||||
uint32_t last_meter_fill;
|
||||
struct timeval last_wheel_motion;
|
||||
int last_wheel_dir;
|
||||
|
||||
Glib::Mutex io_lock;
|
||||
|
||||
int open ();
|
||||
int read (uint8_t *buf,uint32_t timeout_override = 0);
|
||||
int write (uint8_t* cmd, uint32_t timeout_override = 0);
|
||||
int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
|
||||
int close ();
|
||||
int save(char *name = "default");
|
||||
int load(char *name = "default");
|
||||
void print (int row, int col, const char* text);
|
||||
void print_noretry (int row, int col, const char* text);
|
||||
|
||||
int rtpriority_set(int priority = 52);
|
||||
int rtpriority_unset(int priority = 0);
|
||||
|
||||
int open_core (struct usb_device*);
|
||||
|
||||
static void* _monitor_work (void* arg);
|
||||
void* monitor_work ();
|
||||
|
||||
int process (uint8_t *);
|
||||
int update_state();
|
||||
void invalidate();
|
||||
int flush();
|
||||
// bool isuptodate(); // think on this. It seems futile to update more than 30/sec
|
||||
|
||||
// A screen is a cache of what should be on the lcd
|
||||
|
||||
void screen_init();
|
||||
void screen_validate();
|
||||
void screen_invalidate();
|
||||
int screen_flush();
|
||||
void screen_clear();
|
||||
// bool screen_isuptodate(); // think on this -
|
||||
|
||||
// Commands to write to the lcd
|
||||
|
||||
int lcd_init();
|
||||
bool lcd_damage();
|
||||
bool lcd_isdamaged();
|
||||
|
||||
bool lcd_damage(int row, int col = 0, int length = 20);
|
||||
bool lcd_isdamaged(int row, int col = 0, int length = 20);
|
||||
|
||||
int lcd_flush();
|
||||
int lcd_write(uint8_t* cmd, uint32_t timeout_override = 0); // pedantic alias for write
|
||||
void lcd_fill (uint8_t fill_char);
|
||||
void lcd_clear ();
|
||||
void lcd_print (int row, int col, const char* text);
|
||||
void lcd_print_noretry (int row, int col, const char* text);
|
||||
|
||||
// Commands to write to the lights
|
||||
// FIXME - on some devices lights can have intensity and colors
|
||||
|
||||
void lights_init();
|
||||
void lights_validate();
|
||||
void lights_invalidate();
|
||||
void light_validate(LightID light);
|
||||
void light_invalidate(LightID light);
|
||||
int lights_flush();
|
||||
int lights_write(uint8_t* cmd,uint32_t timeout_override = 0); // pedantic alias to write
|
||||
|
||||
// a cache of what should be lit
|
||||
|
||||
void lights_off ();
|
||||
void lights_on ();
|
||||
int light_set(LightID, bool offon = true);
|
||||
int light_on (LightID);
|
||||
int light_off (LightID);
|
||||
|
||||
// some modes for the lights, should probably be renamed
|
||||
|
||||
int lights_show_normal();
|
||||
int lights_show_recording();
|
||||
int lights_show_tempo();
|
||||
int lights_show_bling();
|
||||
|
||||
void enter_big_meter_mode ();
|
||||
void enter_normal_display_mode ();
|
||||
void enter_config_mode();
|
||||
void enter_recording_mode();
|
||||
void enter_bling_mode();
|
||||
|
||||
void next_display_mode ();
|
||||
void normal_update ();
|
||||
|
||||
void show_current_track ();
|
||||
void show_track_gain ();
|
||||
void show_transport_time ();
|
||||
void show_bbt (nframes_t where);
|
||||
void show_smpte (nframes_t where);
|
||||
void show_wheel_mode ();
|
||||
void show_gain ();
|
||||
void show_pan ();
|
||||
void show_meter ();
|
||||
|
||||
void datawheel ();
|
||||
void scrub ();
|
||||
void scroll ();
|
||||
void shuttle ();
|
||||
void config ();
|
||||
|
||||
void next_wheel_mode ();
|
||||
void next_wheel_shift_mode ();
|
||||
|
||||
void set_current_track (ARDOUR::Route*);
|
||||
void next_track ();
|
||||
void prev_track ();
|
||||
void step_gain_up ();
|
||||
void step_gain_down ();
|
||||
void step_pan_right ();
|
||||
void step_pan_left ();
|
||||
|
||||
|
||||
void button_event_battery_press (bool shifted);
|
||||
void button_event_battery_release (bool shifted);
|
||||
void button_event_backlight_press (bool shifted);
|
||||
void button_event_backlight_release (bool shifted);
|
||||
void button_event_trackleft_press (bool shifted);
|
||||
void button_event_trackleft_release (bool shifted);
|
||||
void button_event_trackright_press (bool shifted);
|
||||
void button_event_trackright_release (bool shifted);
|
||||
void button_event_trackrec_press (bool shifted);
|
||||
void button_event_trackrec_release (bool shifted);
|
||||
void button_event_trackmute_press (bool shifted);
|
||||
void button_event_trackmute_release (bool shifted);
|
||||
void button_event_tracksolo_press (bool shifted);
|
||||
void button_event_tracksolo_release (bool shifted);
|
||||
void button_event_undo_press (bool shifted);
|
||||
void button_event_undo_release (bool shifted);
|
||||
void button_event_in_press (bool shifted);
|
||||
void button_event_in_release (bool shifted);
|
||||
void button_event_out_press (bool shifted);
|
||||
void button_event_out_release (bool shifted);
|
||||
void button_event_punch_press (bool shifted);
|
||||
void button_event_punch_release (bool shifted);
|
||||
void button_event_loop_press (bool shifted);
|
||||
void button_event_loop_release (bool shifted);
|
||||
void button_event_prev_press (bool shifted);
|
||||
void button_event_prev_release (bool shifted);
|
||||
void button_event_add_press (bool shifted);
|
||||
void button_event_add_release (bool shifted);
|
||||
void button_event_next_press (bool shifted);
|
||||
void button_event_next_release (bool shifted);
|
||||
void button_event_rewind_press (bool shifted);
|
||||
void button_event_rewind_release (bool shifted);
|
||||
void button_event_fastforward_press (bool shifted);
|
||||
void button_event_fastforward_release (bool shifted);
|
||||
void button_event_stop_press (bool shifted);
|
||||
void button_event_stop_release (bool shifted);
|
||||
void button_event_play_press (bool shifted);
|
||||
void button_event_play_release (bool shifted);
|
||||
void button_event_record_press (bool shifted);
|
||||
void button_event_record_release (bool shifted);
|
||||
|
||||
// new api
|
||||
void button_event_mute (bool pressed, bool shifted);
|
||||
};
|
||||
|
||||
|
||||
#endif // ardour_tranzport_control_protocol_h
|
||||
73
libs/surfaces/mackie/SConscript
Normal file
73
libs/surfaces/mackie/SConscript
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# -*- python -*-
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import glob
|
||||
|
||||
Import('env final_prefix install_prefix final_config_prefix libraries i18n')
|
||||
|
||||
mackie = env.Copy()
|
||||
|
||||
#
|
||||
# this defines the version number of libardour_mackie
|
||||
#
|
||||
|
||||
domain = 'ardour_mackie'
|
||||
|
||||
mackie.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
|
||||
mackie.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
|
||||
mackie.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
|
||||
mackie.Append(PACKAGE = domain)
|
||||
mackie.Append(POTFILE = domain + '.pot')
|
||||
|
||||
mackie_files=Split("""
|
||||
interface.cc
|
||||
midi_byte_array.cc
|
||||
controls.cc
|
||||
route_signal.cc
|
||||
mackie_midi_builder.cc
|
||||
mackie_button_handler.cc
|
||||
mackie_control_protocol_poll.cc
|
||||
surface_port.cc
|
||||
mackie_port.cc
|
||||
types.cc
|
||||
surface.cc
|
||||
mackie_control_protocol.cc
|
||||
bcf_surface.cc
|
||||
mackie_surface.cc
|
||||
""")
|
||||
|
||||
mackie.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
|
||||
mackie.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
|
||||
mackie.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
|
||||
mackie.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
|
||||
|
||||
mackie.Merge ([
|
||||
libraries['ardour'],
|
||||
libraries['ardour_cp'],
|
||||
libraries['sigc2'],
|
||||
libraries['pbd'],
|
||||
libraries['midi++2'],
|
||||
libraries['xml'],
|
||||
libraries['glib2'],
|
||||
libraries['glibmm2']
|
||||
])
|
||||
|
||||
libardour_mackie = mackie.SharedLibrary('ardour_mackie', mackie_files)
|
||||
|
||||
test_files = Split("""
|
||||
midi_byte_array.cc
|
||||
test.cc
|
||||
""")
|
||||
mackie_test = Program('mackie_test', test_files )
|
||||
|
||||
if mackie['SURFACES']:
|
||||
Default(libardour_mackie)
|
||||
if env['NLS']:
|
||||
i18n (mackie, mackie_files, env)
|
||||
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2','surfaces'), libardour_mackie))
|
||||
|
||||
env.Alias('tarball', env.Distribute (env['DISTTREE'],
|
||||
[ 'SConscript' ] +
|
||||
mackie_files +
|
||||
glob.glob('po/*.po') + glob.glob('*.h')))
|
||||
44
libs/surfaces/mackie/TODO
Normal file
44
libs/surfaces/mackie/TODO
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
* how long can UI signal callbacks take to execute? What happens if they block?
|
||||
where ENSURE_CORRECT_THREAD is a macro that is modelled on ENSURE_GUI_THREAD
|
||||
if the handler is not called in the "correct thread", it will use a pseudo-RT-safe-enough technique to get the correct thread to recall "handler" later on, and return.
|
||||
|
||||
* occasional hang on startup. deadlock?
|
||||
* finish button mapping
|
||||
* discuss button mapping for Ardour
|
||||
* concurrency for bank switching? And make sure "old" events aren't sent to "new" faders
|
||||
* concurrency in write( bytes ). Queueing?
|
||||
* Are remote_ids supposed to be 1-based or 0-based?
|
||||
* TODOs in code
|
||||
* handle remote_control_id changed signal from Route.
|
||||
* handle removal of route. Need another session signal?
|
||||
* Some indication on the UI of remote_ids?
|
||||
Useful for surfaces that don't have a scribble strip.
|
||||
* use i18n. see string_compose
|
||||
* MackieControlProtocol in namespace Mackie?
|
||||
* Generic surface code to common location
|
||||
* power-cycling of surface. fd_midiport doesn't close.
|
||||
* remove couts
|
||||
* jog with transport rolling doesn't work properly
|
||||
|
||||
Later
|
||||
-----
|
||||
* which bank switching - overlap or dead faders? Option?
|
||||
* signals for buttons?
|
||||
|
||||
Actual Mackie
|
||||
-------------
|
||||
* docs claim that unit will send a host query on init.
|
||||
* test Mackie surface object. Apparently led rings don't work
|
||||
* timecode & 55 char displays
|
||||
* midi bandwidth
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
* get_state isn't called on deactivate
|
||||
* close existing and load other session doesn't start control surface
|
||||
* set_state is called twice from the control_manager
|
||||
* routes "forget" their remote_id between session save and the next session load
|
||||
* definitely something wrong with remote_id assignment on session create
|
||||
(master strip assigned 0).
|
||||
* gui enables record for tracks with no inputs
|
||||
1458
libs/surfaces/mackie/bcf_surface.cc
Normal file
1458
libs/surfaces/mackie/bcf_surface.cc
Normal file
File diff suppressed because it is too large
Load diff
27
libs/surfaces/mackie/bcf_surface.h
Normal file
27
libs/surfaces/mackie/bcf_surface.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef mackie_surface_bcf_h
|
||||
#define mackie_surface_bcf_h
|
||||
/*
|
||||
Generated by scripts/generate-surface.rb
|
||||
*/
|
||||
|
||||
#include "surface.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler;
|
||||
|
||||
class BcfSurface : public Surface
|
||||
{
|
||||
public:
|
||||
BcfSurface( uint32_t max_strips ) : Surface( max_strips )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
|
||||
virtual void init_controls();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
109
libs/surfaces/mackie/controls.cc
Normal file
109
libs/surfaces/mackie/controls.cc
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "controls.h"
|
||||
#include "types.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
using namespace Mackie;
|
||||
using namespace std;
|
||||
|
||||
void Group::add( Control & control )
|
||||
{
|
||||
_controls.push_back( &control );
|
||||
}
|
||||
|
||||
Strip::Strip( const std::string & name, int index )
|
||||
: Group( name )
|
||||
, _solo( 0 )
|
||||
, _recenable( 0 )
|
||||
, _mute( 0 )
|
||||
, _select( 0 )
|
||||
, _vselect( 0 )
|
||||
, _fader_touch( 0 )
|
||||
, _vpot( 0 )
|
||||
, _gain( 0 )
|
||||
, _index( index )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
generated with
|
||||
|
||||
controls[1].each do |x|
|
||||
puts <<EOF
|
||||
#{x.class.name} & Strip::#{x.name}()
|
||||
{
|
||||
if ( _#{x.name} == 0 )
|
||||
throw MackieControlException( "#{x.name} is null" );
|
||||
return *_#{x.name};
|
||||
}
|
||||
EOF
|
||||
end
|
||||
*/
|
||||
Fader & Strip::gain()
|
||||
{
|
||||
if ( _gain == 0 )
|
||||
throw MackieControlException( "gain is null" );
|
||||
return *_gain;
|
||||
}
|
||||
Pot & Strip::vpot()
|
||||
{
|
||||
if ( _vpot == 0 )
|
||||
throw MackieControlException( "vpot is null" );
|
||||
return *_vpot;
|
||||
}
|
||||
Button & Strip::recenable()
|
||||
{
|
||||
if ( _recenable == 0 )
|
||||
throw MackieControlException( "recenable is null" );
|
||||
return *_recenable;
|
||||
}
|
||||
Button & Strip::solo()
|
||||
{
|
||||
if ( _solo == 0 )
|
||||
throw MackieControlException( "solo is null" );
|
||||
return *_solo;
|
||||
}
|
||||
Button & Strip::mute()
|
||||
{
|
||||
if ( _mute == 0 )
|
||||
throw MackieControlException( "mute is null" );
|
||||
return *_mute;
|
||||
}
|
||||
Button & Strip::select()
|
||||
{
|
||||
if ( _select == 0 )
|
||||
throw MackieControlException( "select is null" );
|
||||
return *_select;
|
||||
}
|
||||
Button & Strip::vselect()
|
||||
{
|
||||
if ( _vselect == 0 )
|
||||
throw MackieControlException( "vselect is null" );
|
||||
return *_vselect;
|
||||
}
|
||||
Button & Strip::fader_touch()
|
||||
{
|
||||
if ( _fader_touch == 0 )
|
||||
throw MackieControlException( "fader_touch is null" );
|
||||
return *_fader_touch;
|
||||
}
|
||||
301
libs/surfaces/mackie/controls.h
Normal file
301
libs/surfaces/mackie/controls.h
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef mackie_controls_h
|
||||
#define mackie_controls_h
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "mackie_control_exception.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class Control;
|
||||
|
||||
/**
|
||||
This is a loose group of controls, eg cursor buttons,
|
||||
transport buttons, functions buttons etc.
|
||||
*/
|
||||
class Group
|
||||
{
|
||||
public:
|
||||
Group( const std::string & name )
|
||||
: _name( name )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Group() {}
|
||||
|
||||
virtual bool is_strip() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool is_master() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void add( Control & control );
|
||||
|
||||
const std::string & name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
// This is for Surface only
|
||||
void name( const std::string & rhs ) { _name = rhs; }
|
||||
|
||||
typedef std::vector<Control*> Controls;
|
||||
const Controls & controls() const { return _controls; }
|
||||
|
||||
protected:
|
||||
Controls _controls;
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class Button;
|
||||
class Pot;
|
||||
class Fader;
|
||||
|
||||
/**
|
||||
This is the set of controls that make up a strip.
|
||||
*/
|
||||
class Strip : public Group
|
||||
{
|
||||
public:
|
||||
Strip( const std::string & name, int index );
|
||||
|
||||
virtual bool is_strip() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void add( Control & control );
|
||||
|
||||
/// This is the index of the strip
|
||||
int index() const { return _index; }
|
||||
|
||||
/// This is for Surface only
|
||||
void index( int rhs ) { _index = rhs; }
|
||||
|
||||
Button & solo();
|
||||
Button & recenable();
|
||||
Button & mute();
|
||||
Button & select();
|
||||
Button & vselect();
|
||||
Button & fader_touch();
|
||||
Pot & vpot();
|
||||
Fader & gain();
|
||||
|
||||
bool has_solo() { return _solo != 0; }
|
||||
bool has_recenable() { return _recenable != 0; }
|
||||
bool has_mute() { return _mute != 0; }
|
||||
bool has_select() { return _select != 0; }
|
||||
bool has_vselect() { return _vselect != 0; }
|
||||
bool has_fader_touch() { return _fader_touch != 0; }
|
||||
bool has_vpot() { return _vpot != 0; }
|
||||
bool has_gain() { return _gain != 0; }
|
||||
|
||||
private:
|
||||
Button * _solo;
|
||||
Button * _recenable;
|
||||
Button * _mute;
|
||||
Button * _select;
|
||||
Button * _vselect;
|
||||
Button * _fader_touch;
|
||||
Pot * _vpot;
|
||||
Fader * _gain;
|
||||
int _index;
|
||||
};
|
||||
|
||||
class MasterStrip : public Strip
|
||||
{
|
||||
public:
|
||||
MasterStrip( const std::string & name, int index )
|
||||
: Strip( name, index )
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool is_master() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Led;
|
||||
|
||||
/**
|
||||
The base class for controls on the surface. They deliberately
|
||||
don't know the midi protocol for updating them.
|
||||
*/
|
||||
class Control
|
||||
{
|
||||
public:
|
||||
enum type_t { type_fader, type_button, type_pot, type_led, type_led_ring };
|
||||
|
||||
Control( int id, int ordinal, std::string name, Group & group )
|
||||
: _id( id ), _ordinal( ordinal ), _name( name ), _group( group )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Control() {}
|
||||
|
||||
virtual const Led & led() const
|
||||
{
|
||||
throw MackieControlException( "no led available" );
|
||||
}
|
||||
|
||||
/// The midi id of the control
|
||||
int id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
/// The 1-based number of the control
|
||||
int ordinal() const
|
||||
{
|
||||
return _ordinal;
|
||||
}
|
||||
|
||||
const std::string & name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
const Group & group() const
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
const Strip & strip() const
|
||||
{
|
||||
return dynamic_cast<const Strip&>( _group );
|
||||
}
|
||||
|
||||
Strip & strip()
|
||||
{
|
||||
return dynamic_cast<Strip&>( _group );
|
||||
}
|
||||
|
||||
virtual bool accepts_feedback() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual type_t type() const = 0;
|
||||
|
||||
private:
|
||||
int _id;
|
||||
int _ordinal;
|
||||
std::string _name;
|
||||
Group & _group;
|
||||
};
|
||||
|
||||
std::ostream & operator << ( std::ostream & os, const Control & control );
|
||||
|
||||
class Fader : public Control
|
||||
{
|
||||
public:
|
||||
Fader( int id, int ordinal, std::string name, Group & group )
|
||||
: Control( id, ordinal, name, group )
|
||||
, _touch( false )
|
||||
{
|
||||
}
|
||||
|
||||
bool touch() const { return _touch; }
|
||||
|
||||
void touch( bool yn ) { _touch = yn; }
|
||||
|
||||
virtual type_t type() const { return type_fader; }
|
||||
|
||||
private:
|
||||
bool _touch;
|
||||
};
|
||||
|
||||
class Led : public Control
|
||||
{
|
||||
public:
|
||||
Led( int id, int ordinal, std::string name, Group & group )
|
||||
: Control( id, ordinal, name, group )
|
||||
{
|
||||
}
|
||||
|
||||
virtual const Led & led() const { return *this; }
|
||||
|
||||
virtual type_t type() const { return type_led; }
|
||||
};
|
||||
|
||||
class Button : public Control
|
||||
{
|
||||
public:
|
||||
Button( int id, int ordinal, std::string name, Group & group )
|
||||
: Control( id, ordinal, name, group )
|
||||
, _led( id, ordinal, name + "_led", group )
|
||||
{
|
||||
}
|
||||
|
||||
virtual const Led & led() const
|
||||
{
|
||||
return _led;
|
||||
}
|
||||
|
||||
virtual type_t type() const { return type_button; };
|
||||
|
||||
private:
|
||||
Led _led;
|
||||
};
|
||||
|
||||
class LedRing : public Led
|
||||
{
|
||||
public:
|
||||
LedRing( int id, int ordinal, std::string name, Group & group )
|
||||
: Led( id, ordinal, name, group )
|
||||
{
|
||||
}
|
||||
|
||||
virtual type_t type() const { return type_led_ring; }
|
||||
};
|
||||
|
||||
class Pot : public Control
|
||||
{
|
||||
public:
|
||||
Pot( int id, int ordinal, std::string name, Group & group )
|
||||
: Control( id, ordinal, name, group )
|
||||
, _led_ring( id, ordinal, name + "_ring", group )
|
||||
{
|
||||
}
|
||||
|
||||
virtual type_t type() const { return type_pot; }
|
||||
|
||||
virtual const LedRing & led_ring() const
|
||||
{
|
||||
return _led_ring;
|
||||
}
|
||||
|
||||
private:
|
||||
LedRing _led_ring;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
76
libs/surfaces/mackie/interface.cc
Normal file
76
libs/surfaces/mackie/interface.cc
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 Paul Davis
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <control_protocol/control_protocol.h>
|
||||
#include "mackie_control_protocol.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
|
||||
ControlProtocol*
|
||||
new_mackie_protocol (ControlProtocolDescriptor* descriptor, Session* s)
|
||||
{
|
||||
MackieControlProtocol * mcp = 0;
|
||||
try
|
||||
{
|
||||
mcp = new MackieControlProtocol (*s);
|
||||
mcp->set_active( true );
|
||||
}
|
||||
catch( exception & e )
|
||||
{
|
||||
cout << "Error instantiating MackieControlProtocol: " << e.what() << endl;
|
||||
delete mcp;
|
||||
mcp = 0;
|
||||
}
|
||||
return mcp;
|
||||
}
|
||||
|
||||
void
|
||||
delete_mackie_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
|
||||
{
|
||||
delete cp;
|
||||
}
|
||||
|
||||
bool
|
||||
probe_mackie_protocol (ControlProtocolDescriptor* descriptor)
|
||||
{
|
||||
return MackieControlProtocol::probe();
|
||||
}
|
||||
|
||||
static ControlProtocolDescriptor mackie_descriptor = {
|
||||
name : "Mackie",
|
||||
id : "uri://ardour.org/surfaces/mackie:0",
|
||||
ptr : 0,
|
||||
module : 0,
|
||||
mandatory : 0,
|
||||
supports_feedback : true,
|
||||
probe : probe_mackie_protocol,
|
||||
initialize : new_mackie_protocol,
|
||||
destroy : delete_mackie_protocol
|
||||
};
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
ControlProtocolDescriptor*
|
||||
protocol_descriptor () {
|
||||
return &mackie_descriptor;
|
||||
}
|
||||
|
||||
}
|
||||
673
libs/surfaces/mackie/mackie_button_handler.cc
Normal file
673
libs/surfaces/mackie/mackie_button_handler.cc
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
Generated by scripts/generate-button-handlers.erb
|
||||
*/
|
||||
#include "mackie_button_handler.h"
|
||||
#include "controls.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
|
||||
LedState MackieButtonHandler::default_button_press( Button & button )
|
||||
{
|
||||
cout << "press: " << button << endl;
|
||||
return on;
|
||||
}
|
||||
LedState MackieButtonHandler::default_button_release( Button & button )
|
||||
{
|
||||
cout << "release: " << button << endl;
|
||||
return off;
|
||||
}
|
||||
|
||||
|
||||
LedState MackieButtonHandler::io_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::io_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::sends_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::sends_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::pan_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::pan_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::plugin_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::plugin_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::eq_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::eq_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::dyn_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::dyn_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::left_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::left_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::right_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::right_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::channel_left_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::channel_left_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::channel_right_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::channel_right_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::flip_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::flip_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::edit_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::edit_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::name_value_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::name_value_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::smpte_beats_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::smpte_beats_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F1_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F1_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F2_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F2_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F3_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F3_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F4_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F4_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F5_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F5_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F6_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F6_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F7_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F7_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F8_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F8_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F9_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F9_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F10_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F10_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F11_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F11_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F12_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F12_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F13_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F13_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F14_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F14_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F15_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F15_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F16_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::F16_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::shift_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::shift_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::option_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::option_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::control_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::control_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cmd_alt_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cmd_alt_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::on_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::on_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::rec_ready_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::rec_ready_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::undo_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::undo_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::snapshot_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::snapshot_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::touch_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::touch_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::redo_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::redo_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::marker_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::marker_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::enter_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::enter_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cancel_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cancel_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::mixer_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::mixer_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::frm_left_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::frm_left_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::frm_right_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::frm_right_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::loop_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::loop_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::punch_in_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::punch_in_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::punch_out_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::punch_out_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::home_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::home_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::end_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::end_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::rewind_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::rewind_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::ffwd_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::ffwd_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::stop_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::stop_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::play_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::play_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::record_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::record_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_up_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_up_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_down_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_down_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_left_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_left_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_right_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::cursor_right_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::zoom_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::zoom_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::scrub_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::scrub_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::user_a_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::user_a_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::user_b_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::user_b_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::fader_touch_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::fader_touch_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
|
||||
222
libs/surfaces/mackie/mackie_button_handler.h
Normal file
222
libs/surfaces/mackie/mackie_button_handler.h
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#ifndef mackie_button_handler_h
|
||||
#define mackie_button_handler_h
|
||||
/*
|
||||
Generated by scripts/generate-button-handlers.erb
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler
|
||||
{
|
||||
public:
|
||||
virtual ~MackieButtonHandler() {}
|
||||
|
||||
virtual LedState default_button_press( Button & button );
|
||||
virtual LedState default_button_release( Button & button );
|
||||
|
||||
virtual void update_led( Button & button, LedState ls ) = 0;
|
||||
|
||||
|
||||
virtual LedState io_press( Button & );
|
||||
virtual LedState io_release( Button & );
|
||||
|
||||
virtual LedState sends_press( Button & );
|
||||
virtual LedState sends_release( Button & );
|
||||
|
||||
virtual LedState pan_press( Button & );
|
||||
virtual LedState pan_release( Button & );
|
||||
|
||||
virtual LedState plugin_press( Button & );
|
||||
virtual LedState plugin_release( Button & );
|
||||
|
||||
virtual LedState eq_press( Button & );
|
||||
virtual LedState eq_release( Button & );
|
||||
|
||||
virtual LedState dyn_press( Button & );
|
||||
virtual LedState dyn_release( Button & );
|
||||
|
||||
virtual LedState left_press( Button & );
|
||||
virtual LedState left_release( Button & );
|
||||
|
||||
virtual LedState right_press( Button & );
|
||||
virtual LedState right_release( Button & );
|
||||
|
||||
virtual LedState channel_left_press( Button & );
|
||||
virtual LedState channel_left_release( Button & );
|
||||
|
||||
virtual LedState channel_right_press( Button & );
|
||||
virtual LedState channel_right_release( Button & );
|
||||
|
||||
virtual LedState flip_press( Button & );
|
||||
virtual LedState flip_release( Button & );
|
||||
|
||||
virtual LedState edit_press( Button & );
|
||||
virtual LedState edit_release( Button & );
|
||||
|
||||
virtual LedState name_value_press( Button & );
|
||||
virtual LedState name_value_release( Button & );
|
||||
|
||||
virtual LedState smpte_beats_press( Button & );
|
||||
virtual LedState smpte_beats_release( Button & );
|
||||
|
||||
virtual LedState F1_press( Button & );
|
||||
virtual LedState F1_release( Button & );
|
||||
|
||||
virtual LedState F2_press( Button & );
|
||||
virtual LedState F2_release( Button & );
|
||||
|
||||
virtual LedState F3_press( Button & );
|
||||
virtual LedState F3_release( Button & );
|
||||
|
||||
virtual LedState F4_press( Button & );
|
||||
virtual LedState F4_release( Button & );
|
||||
|
||||
virtual LedState F5_press( Button & );
|
||||
virtual LedState F5_release( Button & );
|
||||
|
||||
virtual LedState F6_press( Button & );
|
||||
virtual LedState F6_release( Button & );
|
||||
|
||||
virtual LedState F7_press( Button & );
|
||||
virtual LedState F7_release( Button & );
|
||||
|
||||
virtual LedState F8_press( Button & );
|
||||
virtual LedState F8_release( Button & );
|
||||
|
||||
virtual LedState F9_press( Button & );
|
||||
virtual LedState F9_release( Button & );
|
||||
|
||||
virtual LedState F10_press( Button & );
|
||||
virtual LedState F10_release( Button & );
|
||||
|
||||
virtual LedState F11_press( Button & );
|
||||
virtual LedState F11_release( Button & );
|
||||
|
||||
virtual LedState F12_press( Button & );
|
||||
virtual LedState F12_release( Button & );
|
||||
|
||||
virtual LedState F13_press( Button & );
|
||||
virtual LedState F13_release( Button & );
|
||||
|
||||
virtual LedState F14_press( Button & );
|
||||
virtual LedState F14_release( Button & );
|
||||
|
||||
virtual LedState F15_press( Button & );
|
||||
virtual LedState F15_release( Button & );
|
||||
|
||||
virtual LedState F16_press( Button & );
|
||||
virtual LedState F16_release( Button & );
|
||||
|
||||
virtual LedState shift_press( Button & );
|
||||
virtual LedState shift_release( Button & );
|
||||
|
||||
virtual LedState option_press( Button & );
|
||||
virtual LedState option_release( Button & );
|
||||
|
||||
virtual LedState control_press( Button & );
|
||||
virtual LedState control_release( Button & );
|
||||
|
||||
virtual LedState cmd_alt_press( Button & );
|
||||
virtual LedState cmd_alt_release( Button & );
|
||||
|
||||
virtual LedState on_press( Button & );
|
||||
virtual LedState on_release( Button & );
|
||||
|
||||
virtual LedState rec_ready_press( Button & );
|
||||
virtual LedState rec_ready_release( Button & );
|
||||
|
||||
virtual LedState undo_press( Button & );
|
||||
virtual LedState undo_release( Button & );
|
||||
|
||||
virtual LedState snapshot_press( Button & );
|
||||
virtual LedState snapshot_release( Button & );
|
||||
|
||||
virtual LedState touch_press( Button & );
|
||||
virtual LedState touch_release( Button & );
|
||||
|
||||
virtual LedState redo_press( Button & );
|
||||
virtual LedState redo_release( Button & );
|
||||
|
||||
virtual LedState marker_press( Button & );
|
||||
virtual LedState marker_release( Button & );
|
||||
|
||||
virtual LedState enter_press( Button & );
|
||||
virtual LedState enter_release( Button & );
|
||||
|
||||
virtual LedState cancel_press( Button & );
|
||||
virtual LedState cancel_release( Button & );
|
||||
|
||||
virtual LedState mixer_press( Button & );
|
||||
virtual LedState mixer_release( Button & );
|
||||
|
||||
virtual LedState frm_left_press( Button & );
|
||||
virtual LedState frm_left_release( Button & );
|
||||
|
||||
virtual LedState frm_right_press( Button & );
|
||||
virtual LedState frm_right_release( Button & );
|
||||
|
||||
virtual LedState loop_press( Button & );
|
||||
virtual LedState loop_release( Button & );
|
||||
|
||||
virtual LedState punch_in_press( Button & );
|
||||
virtual LedState punch_in_release( Button & );
|
||||
|
||||
virtual LedState punch_out_press( Button & );
|
||||
virtual LedState punch_out_release( Button & );
|
||||
|
||||
virtual LedState home_press( Button & );
|
||||
virtual LedState home_release( Button & );
|
||||
|
||||
virtual LedState end_press( Button & );
|
||||
virtual LedState end_release( Button & );
|
||||
|
||||
virtual LedState rewind_press( Button & );
|
||||
virtual LedState rewind_release( Button & );
|
||||
|
||||
virtual LedState ffwd_press( Button & );
|
||||
virtual LedState ffwd_release( Button & );
|
||||
|
||||
virtual LedState stop_press( Button & );
|
||||
virtual LedState stop_release( Button & );
|
||||
|
||||
virtual LedState play_press( Button & );
|
||||
virtual LedState play_release( Button & );
|
||||
|
||||
virtual LedState record_press( Button & );
|
||||
virtual LedState record_release( Button & );
|
||||
|
||||
virtual LedState cursor_up_press( Button & );
|
||||
virtual LedState cursor_up_release( Button & );
|
||||
|
||||
virtual LedState cursor_down_press( Button & );
|
||||
virtual LedState cursor_down_release( Button & );
|
||||
|
||||
virtual LedState cursor_left_press( Button & );
|
||||
virtual LedState cursor_left_release( Button & );
|
||||
|
||||
virtual LedState cursor_right_press( Button & );
|
||||
virtual LedState cursor_right_release( Button & );
|
||||
|
||||
virtual LedState zoom_press( Button & );
|
||||
virtual LedState zoom_release( Button & );
|
||||
|
||||
virtual LedState scrub_press( Button & );
|
||||
virtual LedState scrub_release( Button & );
|
||||
|
||||
virtual LedState user_a_press( Button & );
|
||||
virtual LedState user_a_release( Button & );
|
||||
|
||||
virtual LedState user_b_press( Button & );
|
||||
virtual LedState user_b_release( Button & );
|
||||
|
||||
virtual LedState fader_touch_press( Button & );
|
||||
virtual LedState fader_touch_release( Button & );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
47
libs/surfaces/mackie/mackie_control_exception.h
Normal file
47
libs/surfaces/mackie/mackie_control_exception.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef mackie_control_exception_h
|
||||
#define mackie_control_exception_h
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieControlException : public std::exception
|
||||
{
|
||||
public:
|
||||
MackieControlException( const std::string & msg )
|
||||
: _msg( msg )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MackieControlException() throw () {}
|
||||
|
||||
const char * what() const throw ()
|
||||
{
|
||||
return _msg.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _msg;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
1293
libs/surfaces/mackie/mackie_control_protocol.cc
Normal file
1293
libs/surfaces/mackie/mackie_control_protocol.cc
Normal file
File diff suppressed because it is too large
Load diff
290
libs/surfaces/mackie/mackie_control_protocol.h
Normal file
290
libs/surfaces/mackie/mackie_control_protocol.h
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef ardour_mackie_control_protocol_h
|
||||
#define ardour_mackie_control_protocol_h
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/session.h>
|
||||
#include <midi++/types.h>
|
||||
|
||||
#include <control_protocol/control_protocol.h>
|
||||
#include "midi_byte_array.h"
|
||||
#include "controls.h"
|
||||
#include "route_signal.h"
|
||||
#include "mackie_button_handler.h"
|
||||
#include "mackie_port.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
class Parser;
|
||||
}
|
||||
|
||||
namespace Mackie {
|
||||
class Surface;
|
||||
}
|
||||
|
||||
/**
|
||||
This handles the plugin duties, and the midi encoding and decoding,
|
||||
and the signal callbacks, mostly from ARDOUR::Route.
|
||||
|
||||
The model of the control surface is handled by classes in controls.h
|
||||
|
||||
What happens is that each strip on the control surface has
|
||||
a corresponding route in ControlProtocol::route_table. When
|
||||
an incoming midi message is signaled, the correct route
|
||||
is looked up, and the relevant changes made to it.
|
||||
|
||||
For each route currently in route_table, there's a RouteSignal object
|
||||
which encapsulates the signals that indicate that there are changes
|
||||
to be sent to the surface. The signals are handled by this class.
|
||||
|
||||
Calls to signal handlers pass a Route object which is used to look
|
||||
up the relevant Strip in Surface. Then the state is retrieved from
|
||||
the Route and encoded as the correct midi message.
|
||||
*/
|
||||
class MackieControlProtocol
|
||||
: public ARDOUR::ControlProtocol
|
||||
, public Mackie::MackieButtonHandler
|
||||
{
|
||||
public:
|
||||
MackieControlProtocol( ARDOUR::Session & );
|
||||
virtual ~MackieControlProtocol();
|
||||
|
||||
int set_active (bool yn);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
static bool probe();
|
||||
|
||||
Mackie::Surface & surface();
|
||||
|
||||
// control events
|
||||
void handle_control_event( Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state );
|
||||
|
||||
// strip/route related stuff
|
||||
public:
|
||||
/// Signal handler for Route::solo
|
||||
void notify_solo_changed( ARDOUR::Route *, Mackie::MackiePort * );
|
||||
/// Signal handler for Route::mute
|
||||
void notify_mute_changed( ARDOUR::Route *, Mackie::MackiePort * );
|
||||
/// Signal handler for Route::record_enable_changed
|
||||
void notify_record_enable_changed( ARDOUR::Route *, Mackie::MackiePort * );
|
||||
/// Signal handler for Route::gain_changed ( from IO )
|
||||
void notify_gain_changed( ARDOUR::Route *, Mackie::MackiePort * );
|
||||
/// Signal handler for Route::name_change
|
||||
void notify_name_changed( void *, ARDOUR::Route *, Mackie::MackiePort * );
|
||||
/// Signal handler from Panner::Change
|
||||
void notify_panner_changed( ARDOUR::Route *, Mackie::MackiePort * );
|
||||
/// Signal handler for new routes added
|
||||
void notify_route_added( ARDOUR::Session::RouteList & );
|
||||
|
||||
/// rebuild the current bank. Called on route added/removed and
|
||||
/// remote id changed.
|
||||
void refresh_current_bank();
|
||||
|
||||
// global buttons (ie button not part of strips)
|
||||
public:
|
||||
// button-related signals
|
||||
void notify_record_state_changed();
|
||||
void notify_transport_state_changed();
|
||||
// mainly to pick up punch-in and punch-out
|
||||
void notify_parameter_changed( const char * );
|
||||
void notify_solo_active_changed( bool );
|
||||
|
||||
// this is called to generate the midi to send in response to
|
||||
// a button press.
|
||||
void update_led( Mackie::Button & button, Mackie::LedState );
|
||||
|
||||
// calls update_led, but looks up the button by name
|
||||
void update_global_button( const std::string & name, Mackie::LedState );
|
||||
|
||||
// transport button handler methods from MackieButtonHandler
|
||||
virtual Mackie::LedState rewind_press( Mackie::Button & );
|
||||
virtual Mackie::LedState rewind_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState ffwd_press( Mackie::Button & );
|
||||
virtual Mackie::LedState ffwd_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState stop_press( Mackie::Button & );
|
||||
virtual Mackie::LedState stop_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState play_press( Mackie::Button & );
|
||||
virtual Mackie::LedState play_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState record_press( Mackie::Button & );
|
||||
virtual Mackie::LedState record_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState loop_press( Mackie::Button & );
|
||||
virtual Mackie::LedState loop_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState punch_in_press( Mackie::Button & );
|
||||
virtual Mackie::LedState punch_in_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState punch_out_press( Mackie::Button & );
|
||||
virtual Mackie::LedState punch_out_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState home_press( Mackie::Button & );
|
||||
virtual Mackie::LedState home_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState end_press( Mackie::Button & );
|
||||
virtual Mackie::LedState end_release( Mackie::Button & );
|
||||
|
||||
// bank switching button handler methods from MackieButtonHandler
|
||||
virtual Mackie::LedState left_press( Mackie::Button & );
|
||||
virtual Mackie::LedState left_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState right_press( Mackie::Button & );
|
||||
virtual Mackie::LedState right_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState channel_left_press( Mackie::Button & );
|
||||
virtual Mackie::LedState channel_left_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState channel_right_press( Mackie::Button & );
|
||||
virtual Mackie::LedState channel_right_release( Mackie::Button & );
|
||||
|
||||
protected:
|
||||
// create instances of MackiePort, depending on what's found in ardour.rc
|
||||
void create_ports();
|
||||
|
||||
// shut down the surface
|
||||
void close();
|
||||
|
||||
// create the Surface object, with the correct number
|
||||
// of strips for the currently connected ports and
|
||||
// hook up the control event notification
|
||||
void initialize_surface();
|
||||
|
||||
// This sets up the notifications and sets the
|
||||
// controls to the correct values
|
||||
void update_surface();
|
||||
|
||||
// connects global (not strip) signals from the Session to here
|
||||
// so the surface can be notified of changes from the other UIs.
|
||||
void connect_session_signals();
|
||||
|
||||
// set all controls to their zero position
|
||||
void zero_all();
|
||||
|
||||
/**
|
||||
Fetch the set of routes to be considered for control by the
|
||||
surface. Excluding master, hidden and control routes, and inactive routes
|
||||
*/
|
||||
typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted;
|
||||
Sorted get_sorted_routes();
|
||||
|
||||
// bank switching
|
||||
void switch_banks( int initial );
|
||||
void prev_track();
|
||||
void next_track();
|
||||
|
||||
// delete all RouteSignal objects connecting Routes to Strips
|
||||
void clear_route_signals();
|
||||
|
||||
/// This is the main MCU port, ie not an extender port
|
||||
const Mackie::MackiePort & mcu_port() const;
|
||||
Mackie::MackiePort & mcu_port();
|
||||
|
||||
typedef std::vector<Mackie::RouteSignal*> RouteSignals;
|
||||
RouteSignals route_signals;
|
||||
|
||||
// return which of the ports a particular route_table
|
||||
// index belongs to
|
||||
Mackie::MackiePort & port_for_id( uint32_t index );
|
||||
|
||||
/**
|
||||
Handle a button press for the control and return whether
|
||||
the corresponding light should be on or off.
|
||||
*/
|
||||
bool handle_strip_button( Mackie::Control &, Mackie::ButtonState, boost::shared_ptr<ARDOUR::Route> );
|
||||
|
||||
// Polling midi port(s) for incoming messages
|
||||
static void* _monitor_work (void* arg);
|
||||
void* monitor_work ();
|
||||
/// rebuild the set of ports for this surface
|
||||
void update_ports();
|
||||
/// Returns true if there is pending data, false otherwise
|
||||
bool poll_ports();
|
||||
/// Trigger the MIDI::Parser
|
||||
void read_ports();
|
||||
|
||||
void add_port( MIDI::Port &, int number );
|
||||
|
||||
/**
|
||||
used by the notify_XXX methods to find
|
||||
a strip corresponding to the Route passed in.
|
||||
*/
|
||||
Mackie::Strip & strip_from_route( ARDOUR::Route * route );
|
||||
|
||||
/// notification from the MackiePorts that their status has changed
|
||||
void handle_port_changed( Mackie::SurfacePort *, bool active );
|
||||
|
||||
/**
|
||||
notification that the port is about to start it's init sequence.
|
||||
We must make sure that before this exits, the port is being polled
|
||||
for new data.
|
||||
*/
|
||||
void handle_port_init( Mackie::SurfacePort * );
|
||||
|
||||
boost::shared_ptr<ARDOUR::Route> master_route();
|
||||
Mackie::Strip & master_strip();
|
||||
|
||||
private:
|
||||
boost::shared_ptr<Mackie::RouteSignal> master_route_signal;
|
||||
|
||||
static const char * default_port_name;
|
||||
|
||||
/// The Midi port(s) connected to the units
|
||||
typedef vector<Mackie::MackiePort*> MackiePorts;
|
||||
MackiePorts _ports;
|
||||
|
||||
// the thread that polls the ports for incoming midi data
|
||||
pthread_t thread;
|
||||
|
||||
/// The initial remote_id of the currently switched in bank.
|
||||
uint32_t _current_initial_bank;
|
||||
|
||||
/// protects the port list, and polling structures
|
||||
Glib::Mutex update_mutex;
|
||||
|
||||
/// Protects set_active, and allows waiting on the poll thread
|
||||
Glib::Cond update_cond;
|
||||
|
||||
// because sigc::trackable doesn't seem to be working
|
||||
std::vector<sigc::connection> _connections;
|
||||
std::back_insert_iterator<std::vector<sigc::connection> > connections_back;
|
||||
|
||||
/// The representation of the physical controls on the surface.
|
||||
Mackie::Surface * _surface;
|
||||
|
||||
/// If a port is opened or closed, this will be
|
||||
/// true until the port configuration is updated;
|
||||
bool _ports_changed;
|
||||
|
||||
struct pollfd * pfd;
|
||||
int nfds;
|
||||
};
|
||||
|
||||
#endif // ardour_mackie_control_protocol_h
|
||||
194
libs/surfaces/mackie/mackie_control_protocol_poll.cc
Normal file
194
libs/surfaces/mackie/mackie_control_protocol_poll.cc
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#include "mackie_control_protocol.h"
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
#include "surface_port.h"
|
||||
|
||||
#include <pbd/pthread_utils.h>
|
||||
#include <pbd/error.h>
|
||||
|
||||
#include <midi++/types.h>
|
||||
#include <midi++/port.h>
|
||||
#include <midi++/manager.h>
|
||||
#include <midi++/port_request.h>
|
||||
#include "i18n.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
using namespace PBD;
|
||||
|
||||
const char * MackieControlProtocol::default_port_name = "mcu";
|
||||
|
||||
bool MackieControlProtocol::probe()
|
||||
{
|
||||
return MIDI::Manager::instance()->port( default_port_name ) != 0;
|
||||
}
|
||||
|
||||
void * MackieControlProtocol::monitor_work()
|
||||
{
|
||||
cout << "MackieControlProtocol::monitor_work" << endl;
|
||||
// What does ThreadCreatedWithRequestSize do?
|
||||
PBD::ThreadCreated (pthread_self(), X_("Mackie"));
|
||||
|
||||
#if 0
|
||||
// it seems to do the "block" on poll less often
|
||||
// with this code disabled
|
||||
struct sched_param rtparam;
|
||||
memset (&rtparam, 0, sizeof (rtparam));
|
||||
rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
|
||||
|
||||
int err;
|
||||
if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
|
||||
// do we care? not particularly.
|
||||
PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror( errno )) << endmsg;
|
||||
}
|
||||
#endif
|
||||
|
||||
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
|
||||
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
||||
|
||||
// read from midi ports
|
||||
cout << "start poll cycle" << endl;
|
||||
while ( true )
|
||||
{
|
||||
update_ports();
|
||||
if ( poll_ports() )
|
||||
{
|
||||
try { read_ports(); }
|
||||
catch ( exception & e ) {
|
||||
cout << "MackieControlProtocol::poll_ports caught exception: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
// provide a cancellation point
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
// these never get called
|
||||
cout << "MackieControlProtocol::poll_ports exiting" << endl;
|
||||
|
||||
delete[] pfd;
|
||||
|
||||
return (void*) 0;
|
||||
}
|
||||
|
||||
void MackieControlProtocol::update_ports()
|
||||
{
|
||||
// create pollfd structures if necessary
|
||||
if ( _ports_changed )
|
||||
{
|
||||
Glib::Mutex::Lock ul( update_mutex );
|
||||
// yes, this is a double-test locking paradigm, or whatever it's called
|
||||
// because we don't *always* need to acquire the lock for the first test
|
||||
if ( _ports_changed )
|
||||
{
|
||||
cout << "MackieControlProtocol::update_ports updating" << endl;
|
||||
if ( pfd != 0 ) delete[] pfd;
|
||||
// TODO This might be a memory leak. How does thread cancellation cleanup work?
|
||||
pfd = new pollfd[_ports.size()];
|
||||
nfds = 0;
|
||||
|
||||
for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
|
||||
{
|
||||
cout << "adding port " << (*it)->port().name() << " to pollfd" << endl;
|
||||
pfd[nfds].fd = (*it)->port().selectable();
|
||||
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
|
||||
++nfds;
|
||||
}
|
||||
_ports_changed = false;
|
||||
}
|
||||
update_cond.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void MackieControlProtocol::read_ports()
|
||||
{
|
||||
/* now read any data on the ports */
|
||||
Glib::Mutex::Lock lock( update_mutex );
|
||||
for ( int p = 0; p < nfds; ++p )
|
||||
{
|
||||
// this will cause handle_midi_any in the MackiePort to be triggered
|
||||
if ( pfd[p].revents & POLLIN > 0 )
|
||||
{
|
||||
lock.release();
|
||||
_ports[p]->read();
|
||||
lock.acquire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MackieControlProtocol::poll_ports()
|
||||
{
|
||||
int timeout = 10; // milliseconds
|
||||
int no_ports_sleep = 10; // milliseconds
|
||||
|
||||
Glib::Mutex::Lock lock( update_mutex );
|
||||
// if there are no ports
|
||||
if ( nfds < 1 )
|
||||
{
|
||||
lock.release();
|
||||
cout << "poll_ports no ports" << endl;
|
||||
usleep( no_ports_sleep * 1000 );
|
||||
return false;
|
||||
}
|
||||
|
||||
int retval = poll( pfd, nfds, timeout );
|
||||
if ( retval < 0 )
|
||||
{
|
||||
// gdb at work, perhaps
|
||||
if ( errno != EINTR )
|
||||
{
|
||||
error << string_compose(_("Mackie MIDI thread poll failed (%1)"), strerror( errno ) ) << endmsg;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return retval > 0;
|
||||
}
|
||||
|
||||
void MackieControlProtocol::handle_port_changed( SurfacePort * port, bool active )
|
||||
{
|
||||
cout << "MackieControlProtocol::handle_port_changed port: " << *port << " active: " << active << endl;
|
||||
if ( active == false )
|
||||
{
|
||||
// port gone away. So stop polling it ASAP
|
||||
{
|
||||
// delete the port instance
|
||||
Glib::Mutex::Lock lock( update_mutex );
|
||||
MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port );
|
||||
if ( it != _ports.end() )
|
||||
{
|
||||
delete *it;
|
||||
_ports.erase( it );
|
||||
}
|
||||
}
|
||||
_ports_changed = true;
|
||||
update_ports();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ports_changed = true;
|
||||
// port added
|
||||
update_ports();
|
||||
update_surface();
|
||||
|
||||
// TODO update bank size
|
||||
|
||||
// rebuild surface
|
||||
}
|
||||
}
|
||||
|
||||
void MackieControlProtocol::handle_port_init( Mackie::SurfacePort * sport )
|
||||
{
|
||||
cout << "MackieControlProtocol::handle_port_init" << endl;
|
||||
_ports_changed = true;
|
||||
update_ports();
|
||||
cout << "MackieControlProtocol::handle_port_init finished" << endl;
|
||||
}
|
||||
173
libs/surfaces/mackie/mackie_midi_builder.cc
Normal file
173
libs/surfaces/mackie/mackie_midi_builder.cc
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "mackie_midi_builder.h"
|
||||
|
||||
#include <typeinfo>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "controls.h"
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
using namespace Mackie;
|
||||
using namespace std;
|
||||
|
||||
MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const ControlState & state )
|
||||
{
|
||||
// TODO do an exact calc for 0.50? To allow manually re-centering the port.
|
||||
|
||||
// center on or off
|
||||
MIDI::byte retval = ( state.pos > 0.45 && state.pos < 0.55 ? 1 : 0 ) << 6;
|
||||
|
||||
// mode
|
||||
retval |= ( mode << 4 );
|
||||
|
||||
// value, but only if off hasn't explicitly been set
|
||||
if ( state.led_state != off )
|
||||
retval += ( int(state.pos * 10.0) + 1 ) & 0x0f; // 0b00001111
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state )
|
||||
{
|
||||
return build_led_ring( pot.led_ring(), state );
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state )
|
||||
{
|
||||
// The other way of doing this:
|
||||
// 0x30 + pot/ring number (0-7)
|
||||
//, 0x30 + led_ring.ordinal() - 1
|
||||
return MidiByteArray ( 3
|
||||
// the control type
|
||||
, midi_pot_id
|
||||
// the id
|
||||
, 0x20 + led_ring.id()
|
||||
// the value
|
||||
, calculate_pot_value( midi_pot_mode_dot, state )
|
||||
);
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_led( const Button & button, LedState ls )
|
||||
{
|
||||
return build_led( button.led(), ls );
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
|
||||
{
|
||||
MIDI::byte state = 0;
|
||||
switch ( ls.state() )
|
||||
{
|
||||
case LedState::on: state = 0x7f; break;
|
||||
case LedState::off: state = 0x00; break;
|
||||
case LedState::none: state = 0x00; break; // actually, this should never happen.
|
||||
case LedState::flashing: state = 0x01; break;
|
||||
}
|
||||
|
||||
return MidiByteArray ( 3
|
||||
, midi_button_id
|
||||
, led.id()
|
||||
, state
|
||||
);
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
|
||||
{
|
||||
int posi = int( 0x3fff * pos );
|
||||
|
||||
return MidiByteArray ( 3
|
||||
, midi_fader_id | fader.id()
|
||||
// lower-order bits
|
||||
, posi & 0x7f
|
||||
// higher-order bits
|
||||
, ( posi >> 7 )
|
||||
);
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
|
||||
{
|
||||
Group::Controls::const_iterator it = strip.controls().begin();
|
||||
MidiByteArray retval;
|
||||
for (; it != strip.controls().end(); ++it )
|
||||
{
|
||||
Control & control = **it;
|
||||
if ( control.accepts_feedback() )
|
||||
retval << zero_control( control );
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::zero_control( const Control & control )
|
||||
{
|
||||
switch( control.type() )
|
||||
{
|
||||
case Control::type_button:
|
||||
return build_led( (Button&)control, off );
|
||||
|
||||
case Control::type_led:
|
||||
return build_led( (Led&)control, off );
|
||||
|
||||
case Control::type_fader:
|
||||
return build_fader( (Fader&)control, 0.0 );
|
||||
|
||||
case Control::type_pot:
|
||||
return build_led_ring( dynamic_cast<const Pot&>( control ), off );
|
||||
|
||||
case Control::type_led_ring:
|
||||
return build_led_ring( dynamic_cast<const LedRing&>( control ), off );
|
||||
|
||||
default:
|
||||
ostringstream os;
|
||||
os << "Unknown control type " << control << " in Strip::zero_control";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
}
|
||||
|
||||
char translate_seven_segment( char achar )
|
||||
{
|
||||
achar = toupper( achar );
|
||||
if ( achar >= 0x40 && achar <= 0x60 )
|
||||
return achar - 0x40;
|
||||
else if ( achar >= 0x21 && achar <= 0x3f )
|
||||
return achar;
|
||||
else
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::two_char_display( const std::string & msg, const std::string & dots )
|
||||
{
|
||||
if ( msg.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: msg must be exactly 2 characters" );
|
||||
if ( dots.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: dots must be exactly 2 characters" );
|
||||
|
||||
MidiByteArray bytes( 5, 0xb0, 0x4a, 0x00, 0x4b, 0x00 );
|
||||
|
||||
// chars are understood by the surface in right-to-left order
|
||||
// could also exchange the 0x4a and 0x4b, above
|
||||
bytes[4] = translate_seven_segment( msg[0] ) + ( dots[0] == '.' ? 0x40 : 0x00 );
|
||||
bytes[2] = translate_seven_segment( msg[1] ) + ( dots[1] == '.' ? 0x40 : 0x00 );
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::two_char_display( unsigned int value, const std::string & dots )
|
||||
{
|
||||
ostringstream os;
|
||||
os << setfill('0') << setw(2) << value % 100;
|
||||
return two_char_display( os.str() );
|
||||
}
|
||||
81
libs/surfaces/mackie/mackie_midi_builder.h
Normal file
81
libs/surfaces/mackie/mackie_midi_builder.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef mackie_midi_builder_h
|
||||
#define mackie_midi_builder_h
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
/**
|
||||
This knows how to build midi messages given a control and
|
||||
a state.
|
||||
*/
|
||||
class MackieMidiBuilder
|
||||
{
|
||||
public:
|
||||
/**
|
||||
The first byte of a midi message from the surface
|
||||
will contain one of these, sometimes bitmasked
|
||||
with the control id
|
||||
*/
|
||||
enum midi_types {
|
||||
midi_fader_id = 0xe0
|
||||
, midi_button_id = 0x90
|
||||
, midi_pot_id = 0xb0
|
||||
};
|
||||
|
||||
/**
|
||||
The LED rings have these modes.
|
||||
*/
|
||||
enum midi_pot_mode {
|
||||
midi_pot_mode_dot = 0
|
||||
, midi_pot_mode_boost_cut = 1
|
||||
, midi_pot_mode_wrap = 2
|
||||
, midi_pot_mode_spread = 3
|
||||
};
|
||||
|
||||
MidiByteArray build_led_ring( const Pot & pot, const ControlState & );
|
||||
MidiByteArray build_led_ring( const LedRing & led_ring, const ControlState & );
|
||||
|
||||
MidiByteArray build_led( const Led & led, LedState ls );
|
||||
MidiByteArray build_led( const Button & button, LedState ls );
|
||||
|
||||
MidiByteArray build_fader( const Fader & fader, float pos );
|
||||
|
||||
/// return bytes that will reset all controls to their zero positions
|
||||
MidiByteArray zero_strip( const Strip & strip );
|
||||
|
||||
// provide bytes to zero the given control
|
||||
MidiByteArray zero_control( const Control & control );
|
||||
|
||||
// display the first 2 chars of the msg in the 2 char display
|
||||
// . is appended to the previous character, so A.B. would
|
||||
// be two characters
|
||||
MidiByteArray two_char_display( const std::string & msg, const std::string & dots = " " );
|
||||
MidiByteArray two_char_display( unsigned int value, const std::string & dots = " " );
|
||||
|
||||
protected:
|
||||
static MIDI::byte calculate_pot_value( midi_pot_mode mode, const ControlState & );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
384
libs/surfaces/mackie/mackie_port.cc
Normal file
384
libs/surfaces/mackie/mackie_port.cc
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "mackie_port.h"
|
||||
|
||||
#include "mackie_control_exception.h"
|
||||
#include "mackie_control_protocol.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "controls.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include <midi++/types.h>
|
||||
#include <midi++/port.h>
|
||||
#include <midi++/alsa_sequencer.h>
|
||||
#include <sigc++/sigc++.h>
|
||||
#include <boost/shared_array.hpp>
|
||||
#include <ardour/configuration.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
|
||||
// The MCU sysex header
|
||||
MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 );
|
||||
|
||||
// The MCU extender sysex header
|
||||
MidiByteArray mackie_sysex_hdr_xt ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11 );
|
||||
|
||||
MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t port_type )
|
||||
: SurfacePort( port, number )
|
||||
, _mcp( mcp )
|
||||
, _port_type( port_type )
|
||||
, _emulation( none )
|
||||
, _initialising( true )
|
||||
{
|
||||
cout << "MackiePort::MackiePort" <<endl;
|
||||
}
|
||||
|
||||
MackiePort::~MackiePort()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
int MackiePort::strips() const
|
||||
{
|
||||
if ( _port_type == mcu )
|
||||
{
|
||||
switch ( _emulation )
|
||||
{
|
||||
// BCF2000 only has 8 faders, so reserve one for master
|
||||
case bcf2000: return 7;
|
||||
case mackie: return 8;
|
||||
case none:
|
||||
default:
|
||||
throw MackieControlException( "MackiePort::strips: don't know what emulation we're using" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// must be an extender, ie no master fader
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
// should really be in MackiePort
|
||||
void MackiePort::open()
|
||||
{
|
||||
cout << "MackiePort::open " << *this << endl;
|
||||
_sysex = port().input()->sysex.connect( ( mem_fun (*this, &MackiePort::handle_midi_sysex) ) );
|
||||
|
||||
// make sure the device is connected
|
||||
init();
|
||||
}
|
||||
|
||||
void MackiePort::close()
|
||||
{
|
||||
// disconnect signals
|
||||
_any.disconnect();
|
||||
_sysex.disconnect();
|
||||
|
||||
// or emit a "closing" signal
|
||||
}
|
||||
|
||||
const MidiByteArray & MackiePort::sysex_hdr() const
|
||||
{
|
||||
switch ( _port_type )
|
||||
{
|
||||
case mcu: return mackie_sysex_hdr;
|
||||
case ext: return mackie_sysex_hdr_xt;
|
||||
}
|
||||
cout << "MackiePort::sysex_hdr _port_type not known" << endl;
|
||||
return mackie_sysex_hdr;
|
||||
}
|
||||
|
||||
Control & MackiePort::lookup_control( const MidiByteArray & bytes )
|
||||
{
|
||||
Control * control = 0;
|
||||
int midi_id = -1;
|
||||
MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
|
||||
switch( midi_type )
|
||||
{
|
||||
// fader
|
||||
case MackieMidiBuilder::midi_fader_id:
|
||||
midi_id = bytes[0] & 0x0f;
|
||||
control = _mcp.surface().faders[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "control for fader" << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
// button
|
||||
case MackieMidiBuilder::midi_button_id:
|
||||
midi_id = bytes[1];
|
||||
control = _mcp.surface().buttons[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "control for button" << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
// pot (jog wheel, external control)
|
||||
case MackieMidiBuilder::midi_pot_id:
|
||||
midi_id = bytes[1] & 0x1f;
|
||||
control = _mcp.surface().pots[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "control for button" << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ostringstream os;
|
||||
os << "Cannot find control for " << bytes;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
return *control;
|
||||
}
|
||||
|
||||
MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiByteArray::iterator end )
|
||||
{
|
||||
MidiByteArray l;
|
||||
back_insert_iterator<MidiByteArray> back ( l );
|
||||
copy( begin, end, back );
|
||||
|
||||
MidiByteArray retval;
|
||||
|
||||
// this is how to calculate the response to the challenge.
|
||||
// from the Logic docs.
|
||||
retval << ( 0x7f & ( l[0] + ( l[1] ^ 0xa ) - l[3] ) );
|
||||
retval << ( 0x7f & ( ( l[2] >> l[3] ) ^ ( l[0] + l[3] ) ) );
|
||||
retval << ( 0x7f & ( l[3] - ( l[2] << 2 ) ^ ( l[0] | l[1] ) ) );
|
||||
retval << ( 0x7f & ( l[1] - l[2] + ( 0xf0 ^ ( l[3] << 4 ) ) ) );
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// not used right now
|
||||
MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
|
||||
{
|
||||
// handle host connection query
|
||||
cout << "host connection query: " << bytes << endl;
|
||||
|
||||
if ( bytes.size() != 18 )
|
||||
{
|
||||
finalise_init( false );
|
||||
ostringstream os;
|
||||
os << "expecting 18 bytes, read " << bytes << " from " << port().name();
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
|
||||
// build and send host connection reply
|
||||
MidiByteArray response;
|
||||
response << 0x02;
|
||||
copy( bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter( response ) );
|
||||
response << calculate_challenge_response( bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4 );
|
||||
return response;
|
||||
}
|
||||
|
||||
// not used right now
|
||||
MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes )
|
||||
{
|
||||
cout << "host_connection_confirmation: " << bytes << endl;
|
||||
|
||||
// decode host connection confirmation
|
||||
if ( bytes.size() != 14 )
|
||||
{
|
||||
finalise_init( false );
|
||||
ostringstream os;
|
||||
os << "expecting 14 bytes, read " << bytes << " from " << port().name();
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
|
||||
// send version request
|
||||
return MidiByteArray( 2, 0x13, 0x00 );
|
||||
}
|
||||
|
||||
void MackiePort::probe_emulation( const MidiByteArray & bytes )
|
||||
{
|
||||
cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl;
|
||||
string version_string;
|
||||
for ( int i = 6; i < 11; ++i ) version_string.append( 1, (char)bytes[i] );
|
||||
cout << "version_string: " << version_string << endl;
|
||||
|
||||
// TODO investigate using serial number. Also, possibly size of bytes might
|
||||
// give an indication. Also, apparently MCU sends non-documented messages
|
||||
// sometimes.
|
||||
if (!_initialising)
|
||||
{
|
||||
cout << "MackiePort::probe_emulation out of sequence." << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// probing doesn't work very well, so just use a config variable
|
||||
// to set the emulation mode
|
||||
bool emulation_ok = false;
|
||||
if ( ARDOUR::Config->get_mackie_emulation() == "bcf" )
|
||||
{
|
||||
_emulation = bcf2000;
|
||||
emulation_ok = true;
|
||||
}
|
||||
else if ( ARDOUR::Config->get_mackie_emulation() == "mcu" )
|
||||
{
|
||||
_emulation = mackie;
|
||||
emulation_ok = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "unknown mackie emulation: " << ARDOUR::Config->get_mackie_emulation() << endl;
|
||||
emulation_ok = false;
|
||||
}
|
||||
|
||||
finalise_init( emulation_ok );
|
||||
}
|
||||
|
||||
void MackiePort::init()
|
||||
{
|
||||
cout << "MackiePort::init" << endl;
|
||||
init_mutex.lock();
|
||||
_initialising = true;
|
||||
|
||||
cout << "MackiePort::lock acquired" << endl;
|
||||
// emit pre-init signal
|
||||
init_event();
|
||||
|
||||
// kick off initialisation. See docs in header file for init()
|
||||
write_sysex ( MidiByteArray (2, 0x13, 0x00 ));
|
||||
}
|
||||
|
||||
void MackiePort::finalise_init( bool yn )
|
||||
{
|
||||
cout << "MackiePort::finalise_init" << endl;
|
||||
|
||||
SurfacePort::active( yn );
|
||||
|
||||
if ( yn )
|
||||
{
|
||||
active_event();
|
||||
|
||||
// start handling messages from controls
|
||||
_any = port().input()->any.connect( ( mem_fun (*this, &MackiePort::handle_midi_any) ) );
|
||||
}
|
||||
_initialising = false;
|
||||
init_cond.signal();
|
||||
init_mutex.unlock();
|
||||
}
|
||||
|
||||
bool MackiePort::wait_for_init()
|
||||
{
|
||||
Glib::Mutex::Lock lock( init_mutex );
|
||||
while ( _initialising )
|
||||
{
|
||||
cout << "MackiePort::wait_for_active waiting" << endl;
|
||||
init_cond.wait( init_mutex );
|
||||
cout << "MackiePort::wait_for_active released" << endl;
|
||||
}
|
||||
cout << "MackiePort::wait_for_active returning" << endl;
|
||||
return SurfacePort::active();
|
||||
}
|
||||
|
||||
void MackiePort::handle_midi_sysex (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
|
||||
{
|
||||
MidiByteArray bytes( count, raw_bytes );
|
||||
cout << "handle_midi_sysex: " << bytes << endl;
|
||||
switch( bytes[5] )
|
||||
{
|
||||
case 0x01:
|
||||
// not used right now
|
||||
write_sysex( host_connection_query( bytes ) );
|
||||
break;
|
||||
case 0x03:
|
||||
// not used right now
|
||||
write_sysex( host_connection_confirmation( bytes ) );
|
||||
break;
|
||||
case 0x04:
|
||||
inactive_event();
|
||||
cout << "host connection error" << bytes << endl;
|
||||
break;
|
||||
case 0x14:
|
||||
probe_emulation( bytes );
|
||||
break;
|
||||
default:
|
||||
cout << "unknown sysex: " << bytes << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// converts midi messages into control_event signals
|
||||
void MackiePort::handle_midi_any (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
|
||||
{
|
||||
MidiByteArray bytes( count, raw_bytes );
|
||||
try
|
||||
{
|
||||
// ignore sysex messages
|
||||
if ( bytes[0] == MIDI::sysex ) return;
|
||||
|
||||
Control & control = lookup_control( bytes );
|
||||
|
||||
// This handles incoming bytes. Outgoing bytes
|
||||
// are sent by the signal handlers.
|
||||
switch ( control.type() )
|
||||
{
|
||||
// fader
|
||||
case Control::type_fader:
|
||||
{
|
||||
// for a BCF2000, max is 7f for high-order byte and 0x70 for low-order byte
|
||||
// According to the Logic docs, these should both be 0x7f.
|
||||
// Although it does mention something about only the top-order
|
||||
// 10 bits out of 14 being used
|
||||
int midi_pos = ( bytes[2] << 7 ) + bytes[1];
|
||||
control_event( *this, control, float(midi_pos) / float(0x3fff) );
|
||||
}
|
||||
break;
|
||||
|
||||
// button
|
||||
case Control::type_button:
|
||||
control_event( *this, control, bytes[2] == 0x7f ? press : release );
|
||||
break;
|
||||
|
||||
// pot (jog wheel, external control)
|
||||
case Control::type_pot:
|
||||
{
|
||||
ControlState state;
|
||||
|
||||
// bytes[2] & 0b01000000 (0x40) give sign
|
||||
int sign = ( bytes[2] & 0x40 ) == 0 ? 1 : -1;
|
||||
// bytes[2] & 0b00111111 (0x3f) gives delta
|
||||
state.ticks = ( bytes[2] & 0x3f) * sign;
|
||||
state.delta = float( state.ticks ) / float( 0x3f );
|
||||
|
||||
control_event( *this, control, state );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cerr << "Do not understand control type " << control;
|
||||
}
|
||||
}
|
||||
catch( MackieControlException & e )
|
||||
{
|
||||
cout << bytes << ' ' << e.what() << endl;
|
||||
}
|
||||
}
|
||||
122
libs/surfaces/mackie/mackie_port.h
Normal file
122
libs/surfaces/mackie/mackie_port.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef mackie_port_h
|
||||
#define mackie_port_h
|
||||
|
||||
#include "surface_port.h"
|
||||
|
||||
#include <midi++/types.h>
|
||||
#include <sigc++/signal.h>
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
class Parser;
|
||||
}
|
||||
|
||||
class MackieControlProtocol;
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackiePort : public SurfacePort
|
||||
{
|
||||
public:
|
||||
enum port_type_t { mcu, ext };
|
||||
enum emulation_t { none, mackie, bcf2000 };
|
||||
|
||||
MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t = mcu );
|
||||
~MackiePort();
|
||||
|
||||
virtual void open();
|
||||
virtual void close();
|
||||
|
||||
/// MCU and extenders have different sysex headers
|
||||
virtual const MidiByteArray & sysex_hdr() const;
|
||||
|
||||
/// Handle device initialisation
|
||||
void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t );
|
||||
|
||||
/// Handle all control messags
|
||||
void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t );
|
||||
|
||||
Control & lookup_control( const MidiByteArray & bytes );
|
||||
|
||||
/// return the number of strips associated with this port
|
||||
virtual int strips() const;
|
||||
|
||||
/// Block until the port has finished initialising, and then return
|
||||
/// whether the intialisation succeeded
|
||||
bool wait_for_init();
|
||||
|
||||
emulation_t emulation() const { return _emulation; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
The initialisation sequence is fairly complex. First a lock is acquired
|
||||
so that a condition can be used to signal the end of the init process.
|
||||
Then a sysex is sent to the device. The response to the sysex
|
||||
is handled by a switch in handle_midi_sysex which calls one of the
|
||||
other methods.
|
||||
|
||||
However, windows DAWs ignore the documented init sequence and so we
|
||||
do too. Thanks to Essox for helping with this.
|
||||
|
||||
So we use the version firmware to figure out what device is on
|
||||
the other end of the cable.
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
Once the device is initialised, finalise_init(true) is called, which
|
||||
releases the lock and signals the condition, and starts handling incoming
|
||||
messages. finalise_init(false) will also release the lock but doesn't
|
||||
start handling messages.
|
||||
*/
|
||||
void finalise_init( bool yn );
|
||||
|
||||
MidiByteArray host_connection_query( MidiByteArray & bytes );
|
||||
MidiByteArray host_connection_confirmation( const MidiByteArray & bytes );
|
||||
|
||||
/**
|
||||
Will set _emulation to what it thinks is correct, based
|
||||
on responses from the device. Or get/set parameters. Or
|
||||
environment variables. Or existence of a file.
|
||||
*/
|
||||
void probe_emulation( const MidiByteArray & bytes );
|
||||
|
||||
private:
|
||||
MackieControlProtocol & _mcp;
|
||||
port_type_t _port_type;
|
||||
sigc::connection _any;
|
||||
sigc::connection _sysex;
|
||||
emulation_t _emulation;
|
||||
|
||||
bool _initialising;
|
||||
Glib::Cond init_cond;
|
||||
Glib::Mutex init_mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
1504
libs/surfaces/mackie/mackie_surface.cc
Normal file
1504
libs/surfaces/mackie/mackie_surface.cc
Normal file
File diff suppressed because it is too large
Load diff
27
libs/surfaces/mackie/mackie_surface.h
Normal file
27
libs/surfaces/mackie/mackie_surface.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef mackie_surface_mackie_h
|
||||
#define mackie_surface_mackie_h
|
||||
/*
|
||||
Generated by scripts/generate-surface.rb
|
||||
*/
|
||||
|
||||
#include "surface.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler;
|
||||
|
||||
class MackieSurface : public Surface
|
||||
{
|
||||
public:
|
||||
MackieSurface( uint32_t max_strips ) : Surface( max_strips )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
|
||||
virtual void init_controls();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
98
libs/surfaces/mackie/midi_byte_array.cc
Normal file
98
libs/surfaces/mackie/midi_byte_array.cc
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
|
||||
MidiByteArray::MidiByteArray( size_t size, MIDI::byte array[] )
|
||||
: std::vector<MIDI::byte>()
|
||||
{
|
||||
for ( size_t i = 0; i < size; ++i )
|
||||
{
|
||||
push_back( array[i] );
|
||||
}
|
||||
}
|
||||
|
||||
MidiByteArray::MidiByteArray( size_t count, MIDI::byte first, ... )
|
||||
: vector<MIDI::byte>()
|
||||
{
|
||||
push_back( first );
|
||||
va_list var_args;
|
||||
va_start( var_args, first );
|
||||
for ( size_t i = 1; i < count; ++i )
|
||||
{
|
||||
MIDI::byte b = va_arg( var_args, int );
|
||||
push_back( b );
|
||||
}
|
||||
va_end( var_args );
|
||||
}
|
||||
|
||||
boost::shared_array<MIDI::byte> MidiByteArray::bytes() const
|
||||
{
|
||||
MIDI::byte * buf = new MIDI::byte[size()];
|
||||
const_iterator it = begin();
|
||||
for( MIDI::byte * ptr = buf; it != end(); ++it )
|
||||
{
|
||||
*ptr++ = *it;
|
||||
}
|
||||
return boost::shared_array<MIDI::byte>( buf );
|
||||
}
|
||||
|
||||
void MidiByteArray::copy( size_t count, MIDI::byte * arr )
|
||||
{
|
||||
for( size_t i = 0; i < count; ++i )
|
||||
{
|
||||
push_back( arr[i] );
|
||||
}
|
||||
}
|
||||
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b )
|
||||
{
|
||||
mba.push_back( b );
|
||||
return mba;
|
||||
}
|
||||
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr )
|
||||
{
|
||||
back_insert_iterator<MidiByteArray> bit( mba );
|
||||
copy( barr.begin(), barr.end(), bit );
|
||||
return mba;
|
||||
}
|
||||
|
||||
ostream & operator << ( ostream & os, const MidiByteArray & mba )
|
||||
{
|
||||
os << "[";
|
||||
char fill = os.fill('0');
|
||||
for( MidiByteArray::const_iterator it = mba.begin(); it != mba.end(); ++it )
|
||||
{
|
||||
if ( it != mba.begin() ) os << " ";
|
||||
os << hex << setw(2) << (int)*it;
|
||||
}
|
||||
os.fill( fill );
|
||||
os << dec;
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
76
libs/surfaces/mackie/midi_byte_array.h
Normal file
76
libs/surfaces/mackie/midi_byte_array.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef midi_byte_array_h
|
||||
#define midi_byte_array_h
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_array.hpp>
|
||||
|
||||
//#include <midi++/types.h>
|
||||
namespace MIDI {
|
||||
typedef unsigned char byte;
|
||||
}
|
||||
|
||||
/**
|
||||
To make building arrays of bytes easier. Thusly:
|
||||
|
||||
MidiByteArray mba;
|
||||
mba << 0xf0 << 0x00 << 0xf7;
|
||||
|
||||
MidiByteArray buf;
|
||||
buf << mba;
|
||||
|
||||
MidiByteArray direct( 3, 0xf0, 0x00, 0xf7 );
|
||||
|
||||
cout << mba << endl;
|
||||
cout << buf << endl;
|
||||
cout << direct << endl;
|
||||
|
||||
will all result in "f0 00 f7" being output to stdout
|
||||
*/
|
||||
class MidiByteArray : public std::vector<MIDI::byte>
|
||||
{
|
||||
public:
|
||||
MidiByteArray() : std::vector<MIDI::byte>() {}
|
||||
|
||||
MidiByteArray( size_t count, MIDI::byte array[] );
|
||||
|
||||
/**
|
||||
Accepts a preceding count, and then a list of bytes
|
||||
*/
|
||||
MidiByteArray( size_t count, MIDI::byte first, ... );
|
||||
|
||||
/// return smart pointer to a copy of the bytes
|
||||
boost::shared_array<MIDI::byte> bytes() const;
|
||||
|
||||
/// copy the given number of bytes from the given array
|
||||
void copy( size_t count, MIDI::byte arr[] );
|
||||
};
|
||||
|
||||
/// append the given byte to the end of the array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b );
|
||||
|
||||
/// append the given array to the end of this array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr );
|
||||
|
||||
/// output the bytes as hex to the given stream
|
||||
std::ostream & operator << ( std::ostream & os, const MidiByteArray & mba );
|
||||
|
||||
#endif
|
||||
97
libs/surfaces/mackie/route_signal.cc
Normal file
97
libs/surfaces/mackie/route_signal.cc
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "route_signal.h"
|
||||
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/track.h>
|
||||
#include <ardour/panner.h>
|
||||
|
||||
#include "mackie_control_protocol.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace Mackie;
|
||||
|
||||
void RouteSignal::connect()
|
||||
{
|
||||
if ( _strip.has_solo() )
|
||||
_solo_changed_connection = _route.solo_control().Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_solo_changed ), &_route, &_port ) );
|
||||
|
||||
if ( _strip.has_mute() )
|
||||
_mute_changed_connection = _route.mute_control().Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_mute_changed ), &_route, &_port ) );
|
||||
|
||||
if ( _strip.has_gain() )
|
||||
_gain_changed_connection = _route.gain_control().Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_gain_changed ), &_route, &_port ) );
|
||||
|
||||
_name_changed_connection = _route.name_changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), &_route, &_port ) );
|
||||
|
||||
if ( _route.panner().size() == 1 )
|
||||
{
|
||||
_panner_changed_connection = _route.panner()[0]->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), &_route, &_port ) );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_record_enable_changed_connection =
|
||||
dynamic_cast<ARDOUR::Track&>( _route ).rec_enable_control().Changed
|
||||
.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_record_enable_changed ), &_route, &_port ) )
|
||||
;
|
||||
}
|
||||
catch ( std::bad_cast & )
|
||||
{
|
||||
// this should catch the dynamic_cast to Track, if what we're working
|
||||
// with can't be record-enabled
|
||||
}
|
||||
|
||||
// TODO
|
||||
// active_changed
|
||||
// SelectedChanged
|
||||
// RemoteControlIDChanged
|
||||
}
|
||||
|
||||
void RouteSignal::disconnect()
|
||||
{
|
||||
_solo_changed_connection.disconnect();
|
||||
_mute_changed_connection.disconnect();
|
||||
_gain_changed_connection.disconnect();
|
||||
_name_changed_connection.disconnect();
|
||||
_panner_changed_connection.disconnect();
|
||||
_record_enable_changed_connection.disconnect();
|
||||
}
|
||||
|
||||
void RouteSignal::notify_all()
|
||||
{
|
||||
void * src = &_route;
|
||||
|
||||
if ( _strip.has_solo() )
|
||||
_mcp.notify_solo_changed( &_route, &_port );
|
||||
|
||||
if ( _strip.has_mute() )
|
||||
_mcp.notify_mute_changed( &_route, &_port );
|
||||
|
||||
if ( _strip.has_gain() )
|
||||
_mcp.notify_gain_changed( &_route, &_port );
|
||||
|
||||
_mcp.notify_name_changed( src, &_route, &_port );
|
||||
|
||||
if ( _strip.has_vpot() )
|
||||
_mcp.notify_panner_changed( &_route, &_port );
|
||||
|
||||
if ( _strip.has_recenable() )
|
||||
_mcp.notify_record_enable_changed( &_route, &_port );
|
||||
}
|
||||
82
libs/surfaces/mackie/route_signal.h
Normal file
82
libs/surfaces/mackie/route_signal.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef route_signal_h
|
||||
#define route_signal_h
|
||||
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
class MackieControlProtocol;
|
||||
|
||||
namespace ARDOUR {
|
||||
class Route;
|
||||
}
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class Strip;
|
||||
class MackiePort;
|
||||
|
||||
/**
|
||||
This class is intended to easily create and destroy the set of
|
||||
connections from a route to a control surface strip. Instantiating
|
||||
it will connect the signals, and destructing it will disconnect
|
||||
the signals.
|
||||
|
||||
TODO deal with routes with >1 panner.
|
||||
*/
|
||||
class RouteSignal
|
||||
{
|
||||
public:
|
||||
RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip, MackiePort & port )
|
||||
: _route( route ), _mcp( mcp ), _strip( strip ), _port( port )
|
||||
{
|
||||
connect();
|
||||
}
|
||||
|
||||
~RouteSignal()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
||||
// call all signal handlers manually
|
||||
void notify_all();
|
||||
|
||||
const ARDOUR::Route & route() const { return _route; }
|
||||
Strip & strip() { return _strip; }
|
||||
|
||||
private:
|
||||
ARDOUR::Route & _route;
|
||||
MackieControlProtocol & _mcp;
|
||||
Strip & _strip;
|
||||
MackiePort & _port;
|
||||
|
||||
sigc::connection _solo_changed_connection;
|
||||
sigc::connection _mute_changed_connection;
|
||||
sigc::connection _record_enable_changed_connection;
|
||||
sigc::connection _gain_changed_connection;
|
||||
sigc::connection _name_changed_connection;
|
||||
sigc::connection _panner_changed_connection;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
32
libs/surfaces/mackie/scripts/bank.rb
Normal file
32
libs/surfaces/mackie/scripts/bank.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#! /usr/bin/ruby
|
||||
|
||||
class Bank
|
||||
attr_accessor :routes, :strips, :current
|
||||
|
||||
def initialize( routes = 17, strips = 8, current = 0 )
|
||||
@routes = routes
|
||||
@strips = strips
|
||||
@current = current
|
||||
end
|
||||
|
||||
def left
|
||||
new_initial = current - routes
|
||||
if new_initial < 0
|
||||
new_initial = 0
|
||||
end
|
||||
current = new_initial
|
||||
self
|
||||
end
|
||||
|
||||
def right
|
||||
delta = routes - ( strips + current ) - 1
|
||||
puts "delta: #{delta}"
|
||||
if delta > strips
|
||||
delta = strips
|
||||
end
|
||||
@current += delta
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
b=Bank.new
|
||||
94
libs/surfaces/mackie/scripts/bcf-controls.csv
Normal file
94
libs/surfaces/mackie/scripts/bcf-controls.csv
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
type,count,group,name,switch,led,id
|
||||
# faders
|
||||
fader,7,strip,gain,1,0,0x00
|
||||
fader,1,master,gain,1,0,0x07
|
||||
|
||||
# pots
|
||||
pot,7,strip,vpot,1,1,0x10
|
||||
pot,1,,jog,1,0,0x17
|
||||
pot,1,,external,1,0,0x2e
|
||||
|
||||
# strip buttons
|
||||
button,7,strip,recenable,1,1,0x18
|
||||
button,7,strip,solo,1,1,0x20
|
||||
button,7,strip,mute,1,1,0x10
|
||||
button,7,strip,select,1,1,0x0
|
||||
button,7,strip,vselect,1,0,0x08
|
||||
|
||||
# overlay buttons
|
||||
button,1,assignment,io,1,1,0x28
|
||||
button,1,assignment,sends,1,1,0x29
|
||||
button,1,assignment,pan,1,1,0x2a
|
||||
button,1,assignment,plugin,1,1,0x2b
|
||||
button,1,assignment,eq,1,1,0x2c
|
||||
button,1,assignment,dyn,1,1,0x2d
|
||||
button,1,bank,left,1,0,0x2e
|
||||
button,1,bank,right,1,0,0x2f
|
||||
button,1,bank,channel_left,1,0,0x30
|
||||
button,1,bank,channel_right,1,0,0x31
|
||||
button,1,,flip,1,1,0x32
|
||||
button,1,,edit,1,1,0x33
|
||||
|
||||
button,1,display,name_value,1,0,0x34
|
||||
button,1,display,smpte_beats,1,0,0x35
|
||||
button,1,,F1,1,0,0x36
|
||||
button,1,,F2,1,0,0x37
|
||||
button,1,,F3,1,0,0x38
|
||||
button,1,,F4,1,0,0x39
|
||||
button,1,,F5,1,0,0x3a
|
||||
button,1,,F6,1,0,0x3b
|
||||
button,1,,F7,1,0,0x3c
|
||||
button,1,,F8,1,0,0x3d
|
||||
button,1,,F9,1,0,0x3e
|
||||
button,1,,F10,1,0,0x3f
|
||||
button,1,,F11,1,0,0x40
|
||||
button,1,,F12,1,0,0x41
|
||||
button,1,,F13,1,0,0x42
|
||||
button,1,,F14,1,0,0x43
|
||||
button,1,,F15,1,0,0x44
|
||||
button,1,,F16,1,0,0x45
|
||||
button,1,modifiers,shift,1,0,0x46
|
||||
button,1,modifiers,option,1,0,0x47
|
||||
button,1,modifiers,control,1,0,0x48
|
||||
button,1,modifiers,cmd_alt,1,0,0x49
|
||||
button,1,automation,on,1,1,0x4a
|
||||
button,1,automation,rec_ready,1,1,0x4b
|
||||
button,1,functions,undo,1,1,0x4c
|
||||
button,1,automation,snapshot,1,1,0x4d
|
||||
button,1,automation,touch,1,1,0x4e
|
||||
button,1,functions,redo,1,1,0x4f
|
||||
button,1,functions,marker,1,1,0x50
|
||||
button,1,functions,enter,1,1,0x51
|
||||
button,1,functions,cancel,1,0,0x52
|
||||
button,1,functions,mixer,1,0,0x53
|
||||
button,1,transport,frm_left,1,1,0x54
|
||||
button,1,transport,frm_right,1,1,0x55
|
||||
button,1,transport,loop,1,1,0x56
|
||||
button,1,transport,punch_in,1,1,0x57
|
||||
button,1,transport,punch_out,1,1,0x58
|
||||
button,1,transport,home,1,1,0x59
|
||||
button,1,transport,end,1,1,0x5a
|
||||
|
||||
# transport buttons
|
||||
button,1,transport,"rewind",1,1,0x5b
|
||||
button,1,transport,"ffwd",1,1,0x5c
|
||||
button,1,transport,"stop",1,1,0x5d
|
||||
button,1,transport,"play",1,1,0x5e
|
||||
button,1,transport,"record",1,1,0x1f
|
||||
button,1,cursor,"cursor_up",1,0,0x60
|
||||
button,1,cursor,"cursor_down",1,0,0x61
|
||||
button,1,cursor,"cursor_left",1,0,0x62
|
||||
button,1,cursor,"cursor_right",1,0,0x63
|
||||
button,1,,"zoom",1,1,0x64
|
||||
button,1,,"scrub",1,1,0x65
|
||||
button,1,user,"user_a",1,0,0x66
|
||||
button,1,user,"user_b",1,0,0x67
|
||||
|
||||
button,7,strip,"fader_touch",1,0,0x68
|
||||
button,1,master,"fader_touch",1,0,0x6f
|
||||
button,1,master,mute,1,0,0x17
|
||||
|
||||
button,1,,"smpte",0,1,0x71
|
||||
button,1,,"beats",0,1,0x72
|
||||
button,1,,"solo",0,1,0x73
|
||||
button,1,,"relay_click",0,1,0x76
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
208
libs/surfaces/mackie/scripts/controls.rb
Normal file
208
libs/surfaces/mackie/scripts/controls.rb
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#! /usr/bin/ruby
|
||||
# Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
require 'faster_csv'
|
||||
require 'mackie.rb'
|
||||
|
||||
class Control
|
||||
attr_accessor :id, :led, :group, :name, :ordinal, :switch
|
||||
|
||||
def initialize( obj, group )
|
||||
@id = obj.id
|
||||
@name = obj.name
|
||||
@ordinal = obj.ordinal
|
||||
@switch = obj.switch
|
||||
@group = group
|
||||
end
|
||||
|
||||
def ordinal_name
|
||||
end
|
||||
end
|
||||
|
||||
class Fader < Control
|
||||
def self.midi_zero_byte
|
||||
0xe0
|
||||
end
|
||||
|
||||
def self.mask_for_id( bytes )
|
||||
bytes[0] & 0b00001111
|
||||
end
|
||||
end
|
||||
|
||||
class Button < Control
|
||||
def self.midi_zero_byte
|
||||
0x90
|
||||
end
|
||||
|
||||
def self.mask_for_id( bytes )
|
||||
bytes[1]
|
||||
end
|
||||
end
|
||||
|
||||
class Led < Control
|
||||
end
|
||||
|
||||
class LedRing < Led
|
||||
end
|
||||
|
||||
class Pot < Control
|
||||
def self.midi_zero_byte
|
||||
0xb0
|
||||
end
|
||||
|
||||
def self.mask_for_id( bytes )
|
||||
bytes[1] & 0b00011111
|
||||
end
|
||||
|
||||
def led=( rhs )
|
||||
@led = LedRing.new( rhs, group )
|
||||
end
|
||||
end
|
||||
|
||||
class Group < Array
|
||||
attr_accessor :name, :controls
|
||||
|
||||
def initialize( name )
|
||||
@name = name
|
||||
end
|
||||
|
||||
def add_control( control )
|
||||
@controls ||= Array.new
|
||||
@controls << control
|
||||
end
|
||||
end
|
||||
|
||||
class Strip < Group
|
||||
|
||||
attr_accessor :ordinal
|
||||
def initialize( name, ordinal )
|
||||
super( name )
|
||||
@ordinal = ordinal
|
||||
end
|
||||
|
||||
def name
|
||||
@name == 'master' ? @name : "#{@name}_#{@ordinal}"
|
||||
end
|
||||
|
||||
def is_master
|
||||
name == 'master'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
types = { 0xe0 => Fader, 0x90 => Button, 0xb0 => Pot }
|
||||
|
||||
# number of controls, name, switch, led, id
|
||||
# anything that doesn't have the correct number
|
||||
# of columns will be ignored
|
||||
# actually, 'switch' means it generates data
|
||||
# whereas 'led' means it receives data
|
||||
|
||||
class Row
|
||||
attr_accessor :count, :name, :switch, :led, :start_id, :type, :group
|
||||
attr_accessor :id, :ordinal_name, :ordinal_group, :ordinal
|
||||
|
||||
def initialize( hash )
|
||||
@count = hash['count'].to_i
|
||||
@name = hash['name']
|
||||
@switch = hash['switch'].to_b
|
||||
@led = hash['led'].to_b
|
||||
@start_id = hash['id'].hex
|
||||
@type = hash['type']
|
||||
@group = hash['group']
|
||||
|
||||
@hash = hash
|
||||
end
|
||||
|
||||
def each_ordinal( &block )
|
||||
for i in 0...count
|
||||
@ordinal = i + 1
|
||||
@ordinal_name = count > 1 ? "#{name}_#{ordinal}" : name
|
||||
@ordinal_group = count > 1 ? "#{group}_#{ordinal}" : group
|
||||
@id = start_id + i
|
||||
|
||||
@hash['ordinal_name'] = @ordinal_name
|
||||
@hash['ordinal_group'] = @ordinal_group
|
||||
|
||||
yield( self )
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def to_hash
|
||||
@hash
|
||||
end
|
||||
end
|
||||
|
||||
class Surface
|
||||
attr_reader :groups, :controls_by_id, :types, :midis, :controls, :name
|
||||
|
||||
def initialize( name = 'none' )
|
||||
@name = name
|
||||
@types = Hash.new
|
||||
@groups = Hash.new
|
||||
@controls = Array.new
|
||||
@controls_by_id = Hash.new
|
||||
@midis = Hash.new
|
||||
end
|
||||
|
||||
def add_or_create_group( name, ordinal = nil )
|
||||
if name.nil?
|
||||
@groups['none'] = Group.new('none')
|
||||
else
|
||||
group = name =~ /strip/ || name == 'master' ? Strip.new( name, ordinal ) : Group.new( name )
|
||||
@groups[group.name] ||= group
|
||||
end
|
||||
end
|
||||
|
||||
def parse( control_data )
|
||||
FasterCSV.parse( control_data, :headers => true ) do |csv_row|
|
||||
next if csv_row.entries.size < 5 || csv_row[0] =~ /^\s*#/ || csv_row['id'].nil?
|
||||
row = Row.new( csv_row )
|
||||
|
||||
row.each_ordinal do |row|
|
||||
group = add_or_create_group( row.group, row.ordinal )
|
||||
if row.switch
|
||||
# for controls
|
||||
control = eval "#{row.type.capitalize}.new( row, group )"
|
||||
|
||||
# for controls with leds
|
||||
control.led = Led.new( row, group ) if row.led
|
||||
else
|
||||
# for LED-only entries
|
||||
if row.led
|
||||
control = Led.new( row, group )
|
||||
control.led = control
|
||||
end
|
||||
end
|
||||
|
||||
# add the new control to the various lookups
|
||||
@controls_by_id[row.id] = control
|
||||
@controls << control
|
||||
group << control
|
||||
|
||||
# add incoming midi bytes
|
||||
if row.switch
|
||||
types[control.class.midi_zero_byte] = control.class
|
||||
midis[control.class.midi_zero_byte] ||= Hash.new
|
||||
midis[control.class.midi_zero_byte][row.id] = control
|
||||
end
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
end
|
||||
11
libs/surfaces/mackie/scripts/dump.rb
Executable file
11
libs/surfaces/mackie/scripts/dump.rb
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#! /usr/bin/ruby
|
||||
|
||||
while !File.exist? ARGV[0]
|
||||
sleep 0.010
|
||||
end
|
||||
|
||||
file = File.open ARGV[0], 'r'
|
||||
|
||||
while bytes = file.sysread( 3 )
|
||||
puts "%02x %02x %02x" % [ bytes[0], bytes[1], bytes[2] ]
|
||||
end
|
||||
59
libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb
Normal file
59
libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<%#
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
-%>
|
||||
<%-
|
||||
require 'controls.rb'
|
||||
|
||||
sf = Surface.new
|
||||
sf.parse( File.open "mackie-controls.csv" )
|
||||
buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
|
||||
-%>
|
||||
/*
|
||||
Generated by scripts/generate-button-handlers.erb
|
||||
*/
|
||||
#include "mackie_button_handler.h"
|
||||
#include "controls.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
|
||||
LedState MackieButtonHandler::default_button_press( Button & button )
|
||||
{
|
||||
cout << "press: " << button << endl;
|
||||
return on;
|
||||
}
|
||||
LedState MackieButtonHandler::default_button_release( Button & button )
|
||||
{
|
||||
cout << "release: " << button << endl;
|
||||
return off;
|
||||
}
|
||||
|
||||
<%-
|
||||
buttons.each do |button|
|
||||
%>
|
||||
LedState MackieButtonHandler::<%=button.name%>_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::<%=button.name%>_release( Button & button )
|
||||
{
|
||||
return default_button_release( button );
|
||||
}
|
||||
<% end %>
|
||||
54
libs/surfaces/mackie/scripts/generate-button-handlers-h.erb
Normal file
54
libs/surfaces/mackie/scripts/generate-button-handlers-h.erb
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<%#
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
-%>
|
||||
<%-
|
||||
require 'controls.rb'
|
||||
|
||||
sf = Surface.new
|
||||
sf.parse( File.open "mackie-controls.csv" )
|
||||
buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
|
||||
-%>
|
||||
#ifndef mackie_button_handler_h
|
||||
#define mackie_button_handler_h
|
||||
/*
|
||||
Generated by scripts/generate-button-handlers.erb
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler
|
||||
{
|
||||
public:
|
||||
virtual ~MackieButtonHandler() {}
|
||||
|
||||
virtual LedState default_button_press( Button & button );
|
||||
virtual LedState default_button_release( Button & button );
|
||||
|
||||
virtual void update_led( Button & button, LedState ls ) = 0;
|
||||
|
||||
<%- buttons.each do |button| %>
|
||||
virtual LedState <%=button.name%>_press( Button & );
|
||||
virtual LedState <%=button.name%>_release( Button & );
|
||||
<% end %>
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
25
libs/surfaces/mackie/scripts/generate-surface.rb
Executable file
25
libs/surfaces/mackie/scripts/generate-surface.rb
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#! /usr/bin/ruby
|
||||
|
||||
require 'erb'
|
||||
require 'controls.rb'
|
||||
|
||||
cc_template = ''
|
||||
File.open("surface-cc-template.erb", "r") { |f| cc_template = f.read }
|
||||
|
||||
h_template = ''
|
||||
File.open("surface-h-template.erb", "r") { |f| h_template = f.read }
|
||||
|
||||
sf = Surface.new( ARGV[0] )
|
||||
control_data = ''
|
||||
File.open("#{sf.name.downcase}-controls.csv", "r") { |f| control_data = f.read }
|
||||
sf.parse control_data
|
||||
|
||||
@result = ""
|
||||
erb = ERB.new( cc_template , 0, "%<>-", "@result" )
|
||||
erb.result
|
||||
File.open( "#{sf.name.downcase}_surface.cc", "w" ) { |f| f.write @result }
|
||||
|
||||
erb = ERB.new( h_template , 0, "%<>-", "@result" )
|
||||
erb.result
|
||||
File.open( "#{sf.name.downcase}_surface.h", "w" ) { |f| f.write @result }
|
||||
|
||||
128
libs/surfaces/mackie/scripts/host.rb
Executable file
128
libs/surfaces/mackie/scripts/host.rb
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
#! /usr/bin/ruby
|
||||
# Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
require 'controls.rb'
|
||||
require 'mackie.rb'
|
||||
|
||||
while !File.exist? ARGV[0]
|
||||
sleep 0.010
|
||||
end
|
||||
|
||||
file = File.open ARGV[0], 'r+'
|
||||
mck = Mackie.new( file )
|
||||
|
||||
# send device query
|
||||
response = mck.sysex( "\x00" )
|
||||
puts "response: #{response.to_hex}"
|
||||
|
||||
# decode host connection query
|
||||
status = response[0]
|
||||
if status != 1
|
||||
puts "expected 01, got " + response.to_hex.inspect
|
||||
exit(1)
|
||||
end
|
||||
serial = response[1..7]
|
||||
challenge = response[8..11]
|
||||
puts <<EOF
|
||||
serial: #{serial.to_hex.inspect}
|
||||
challenge: #{challenge.to_hex.inspect}
|
||||
EOF
|
||||
|
||||
# send host connection reply
|
||||
response = mck.sysex( "\x02" + serial.pack('C*') + challenge.pack('C*') )
|
||||
|
||||
# decode host connection confirmation
|
||||
status = response[0]
|
||||
if status != 3
|
||||
puts "expected 03, got " + response.to_hex.inspect
|
||||
exit(1)
|
||||
end
|
||||
|
||||
serial = response[1..7]
|
||||
puts <<EOF
|
||||
serial: #{serial.to_hex.inspect}
|
||||
EOF
|
||||
|
||||
# faders to minimum. bcf2000 doesn't respond
|
||||
#file.write( hdr + "\x61\xf7" )
|
||||
|
||||
# all leds off. bcf2000 doesn't respond
|
||||
#file.write( hdr + "\x62\xf7" )
|
||||
|
||||
# get version. comes back as ASCII bytes
|
||||
version = mck.sysex( "\x13\x00" )
|
||||
puts "version: #{version.map{|x| x.chr}}"
|
||||
|
||||
# write a welcome message. bcf2000 responds with exact
|
||||
# string but doesn't display anything
|
||||
# 0 offset,
|
||||
#~ file.write hdr + "\x12\x3fLCDE\xf7"
|
||||
#~ file.flush
|
||||
#~ answer = read_sysex file
|
||||
#~ puts "answer: #{answer[hdr.length..-1].map{|x| x.chr}}"
|
||||
|
||||
# write to BBT display
|
||||
#~ file.write hdr + "\x10LCDE\xf7"
|
||||
#~ file.flush
|
||||
#~ bbt = []
|
||||
#~ while ( nc = file.read( 1 ) )[0] != 0xf7
|
||||
#~ bbt << nc[0]
|
||||
#~ end
|
||||
#~ puts "bbt: #{bbt[hdr.length..-1].map{|x| x.chr}}"
|
||||
|
||||
# write 7-segment display
|
||||
#~ file.write hdr + "\x11LCDE\xf7"
|
||||
#~ file.flush
|
||||
|
||||
# go offline. bcf2000 doesn't respond
|
||||
#~ file.write( hdr + "\x0f\x7f\xf7" )
|
||||
#~ file.flush
|
||||
|
||||
sf = Surface.new
|
||||
control_data = ""
|
||||
File.open( "mackie-controls.csv" ) { |f| control_data = f.read }
|
||||
sf.parse( control_data )
|
||||
|
||||
# send all faders to 0, but bounce them first
|
||||
# otherwise the bcf gets confused
|
||||
sf.midis[0xe0].values.find_all{|x| x.class == Fader}.each do |x|
|
||||
bytes = Array.new
|
||||
bytes[0] = 0xe0 + x.ordinal - 1
|
||||
bytes[1] = 0x1
|
||||
bytes[2] = 0x1
|
||||
file.write bytes.pack( 'C*' )
|
||||
bytes[0] = 0xe0 + x.ordinal - 1
|
||||
bytes[1] = 0x0
|
||||
bytes[2] = 0x0
|
||||
file.write bytes.pack( 'C*' )
|
||||
end
|
||||
file.flush
|
||||
|
||||
# respond to control movements
|
||||
while bytes = mck.file.read( 3 )
|
||||
print "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ]
|
||||
midi_type = bytes[0] & 0b11110000
|
||||
|
||||
control_id = sf.types[midi_type].mask_for_id( bytes )
|
||||
control = sf.midis[midi_type][control_id]
|
||||
|
||||
print " Control Type: %-7s, " % sf.types[midi_type]
|
||||
print "id: %4i" % control_id
|
||||
print ", control: %15s" % control.name
|
||||
print ", %15s" % control.group.name
|
||||
print "\n"
|
||||
end
|
||||
93
libs/surfaces/mackie/scripts/mackie-controls.csv
Normal file
93
libs/surfaces/mackie/scripts/mackie-controls.csv
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
type,count,group,name,switch,led,id
|
||||
# faders
|
||||
fader,8,strip,gain,1,0,0x00
|
||||
fader,1,master,gain,1,0,0x08
|
||||
|
||||
# pots
|
||||
pot,8,strip,vpot,1,1,0x10
|
||||
pot,1,,jog,1,0,0x3c
|
||||
pot,1,,external,1,0,0x2e
|
||||
|
||||
# strip buttons
|
||||
button,8,strip,recenable,1,1,0x0
|
||||
button,8,strip,solo,1,1,0x08
|
||||
button,8,strip,mute,1,1,0x10
|
||||
button,8,strip,select,1,1,0x18
|
||||
button,8,strip,vselect,1,0,0x20
|
||||
|
||||
# overlay buttons
|
||||
button,1,assignment,io,1,1,0x28
|
||||
button,1,assignment,sends,1,1,0x29
|
||||
button,1,assignment,pan,1,1,0x2a
|
||||
button,1,assignment,plugin,1,1,0x2b
|
||||
button,1,assignment,eq,1,1,0x2c
|
||||
button,1,assignment,dyn,1,1,0x2d
|
||||
button,1,bank,left,1,0,0x2e
|
||||
button,1,bank,right,1,0,0x2f
|
||||
button,1,bank,channel_left,1,0,0x30
|
||||
button,1,bank,channel_right,1,0,0x31
|
||||
button,1,,flip,1,1,0x32
|
||||
button,1,,edit,1,1,0x33
|
||||
|
||||
button,1,display,name_value,1,0,0x34
|
||||
button,1,display,smpte_beats,1,0,0x35
|
||||
button,1,,F1,1,0,0x36
|
||||
button,1,,F2,1,0,0x37
|
||||
button,1,,F3,1,0,0x38
|
||||
button,1,,F4,1,0,0x39
|
||||
button,1,,F5,1,0,0x3a
|
||||
button,1,,F6,1,0,0x3b
|
||||
button,1,,F7,1,0,0x3c
|
||||
button,1,,F8,1,0,0x3d
|
||||
button,1,,F9,1,0,0x3e
|
||||
button,1,,F10,1,0,0x3f
|
||||
button,1,,F11,1,0,0x40
|
||||
button,1,,F12,1,0,0x41
|
||||
button,1,,F13,1,0,0x42
|
||||
button,1,,F14,1,0,0x43
|
||||
button,1,,F15,1,0,0x44
|
||||
button,1,,F16,1,0,0x45
|
||||
button,1,modifiers,shift,1,0,0x46
|
||||
button,1,modifiers,option,1,0,0x47
|
||||
button,1,modifiers,control,1,0,0x48
|
||||
button,1,modifiers,cmd_alt,1,0,0x49
|
||||
button,1,automation,on,1,1,0x4a
|
||||
button,1,automation,rec_ready,1,1,0x4b
|
||||
button,1,functions,undo,1,1,0x4c
|
||||
button,1,automation,snapshot,1,1,0x4d
|
||||
button,1,automation,touch,1,1,0x4e
|
||||
button,1,functions,redo,1,1,0x4f
|
||||
button,1,functions,marker,1,1,0x50
|
||||
button,1,functions,enter,1,1,0x51
|
||||
button,1,functions,cancel,1,0,0x52
|
||||
button,1,functions,mixer,1,0,0x53
|
||||
button,1,transport,frm_left,1,1,0x54
|
||||
button,1,transport,frm_right,1,1,0x55
|
||||
button,1,transport,loop,1,1,0x56
|
||||
button,1,transport,punch_in,1,1,0x57
|
||||
button,1,transport,punch_out,1,1,0x58
|
||||
button,1,transport,home,1,1,0x59
|
||||
button,1,transport,end,1,1,0x5a
|
||||
|
||||
# transport buttons
|
||||
button,1,transport,"rewind",1,1,0x5b
|
||||
button,1,transport,"ffwd",1,1,0x5c
|
||||
button,1,transport,"stop",1,1,0x5d
|
||||
button,1,transport,"play",1,1,0x5e
|
||||
button,1,transport,"record",1,1,0x5f
|
||||
button,1,cursor,"cursor_up",1,0,0x60
|
||||
button,1,cursor,"cursor_down",1,0,0x61
|
||||
button,1,cursor,"cursor_left",1,0,0x62
|
||||
button,1,cursor,"cursor_right",1,0,0x63
|
||||
button,1,,"zoom",1,1,0x64
|
||||
button,1,,"scrub",1,1,0x65
|
||||
button,1,user,"user_a",1,0,0x66
|
||||
button,1,user,"user_b",1,0,0x67
|
||||
|
||||
button,8,strip,"fader_touch",1,0,0x68
|
||||
button,1,master,"fader_touch",1,0,0x70
|
||||
|
||||
button,1,,"smpte",0,1,0x71
|
||||
button,1,,"beats",0,1,0x72
|
||||
button,1,,"solo",0,1,0x73
|
||||
button,1,,"relay_click",0,1,0x76
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
119
libs/surfaces/mackie/scripts/mackie.rb
Normal file
119
libs/surfaces/mackie/scripts/mackie.rb
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
class String
|
||||
def to_bytes
|
||||
arr = []
|
||||
each_byte{|x| arr << x}
|
||||
arr
|
||||
end
|
||||
end
|
||||
|
||||
class Array
|
||||
def to_hex
|
||||
map{|x| "%2.0x" % x}
|
||||
end
|
||||
|
||||
alias as_hex to_hex
|
||||
end
|
||||
|
||||
class String
|
||||
def to_b
|
||||
to_i != 0 || %w{true t yes y}.include?( self.downcase )
|
||||
end
|
||||
end
|
||||
|
||||
class Fixnum
|
||||
def to_hex
|
||||
"%02x" % self
|
||||
end
|
||||
end
|
||||
|
||||
class Mackie
|
||||
attr_accessor :file
|
||||
|
||||
def initialize( file )
|
||||
@file = file
|
||||
end
|
||||
|
||||
# send and receive a sysex message
|
||||
# after wrapping in the header and the eox byte
|
||||
def sysex( msg )
|
||||
puts "Mackie write: #{msg.unpack('C*').to_hex.inspect}"
|
||||
write_sysex( msg )
|
||||
response = read_sysex
|
||||
puts "Mackie response: #{response.to_hex.inspect}"
|
||||
response[5..-1]
|
||||
end
|
||||
|
||||
# returns an array of bytes
|
||||
def read_sysex
|
||||
buf = []
|
||||
while ( nc = @file.read( 1 ) )[0] != 0xf7
|
||||
buf << nc[0]
|
||||
end
|
||||
buf
|
||||
end
|
||||
|
||||
# send and flush a sysex message
|
||||
# after wrapping in the header and the eox byte
|
||||
def write_sysex( msg )
|
||||
@file.write( hdrlc + msg + "\xf7" )
|
||||
@file.flush
|
||||
end
|
||||
|
||||
def write( msg )
|
||||
@file.write msg
|
||||
@file.flush
|
||||
end
|
||||
|
||||
def translate_seven_segment( char )
|
||||
case char
|
||||
when 0x40..0x60
|
||||
char - 0x40
|
||||
when 0x21..0x3f
|
||||
char
|
||||
else
|
||||
0x00
|
||||
end
|
||||
end
|
||||
|
||||
# display the msg (which can be only 2 characters)
|
||||
# append the number of stops. Options are '..', '. ', '. ', ' '
|
||||
def two_char( msg, stops = ' ' )
|
||||
two = Array.new
|
||||
two << translate_seven_segment( msg.upcase[0] )
|
||||
two << translate_seven_segment( msg.upcase[1] )
|
||||
|
||||
two[0] += 0x40 if stops[0] == '.'[0]
|
||||
two[1] += 0x40 if stops[1] == '.'[0]
|
||||
|
||||
midi_msg = [0xb0, 0x4a, two[1], 0x4b, two[0] ]
|
||||
write midi_msg.pack( 'C*' )
|
||||
end
|
||||
|
||||
# send and receive the device initialisation
|
||||
def init
|
||||
response = sysex( "\x00" )
|
||||
|
||||
# decode host connection query
|
||||
status = response[0]
|
||||
raise( "expected 01, got " + response.inspect ) if status != 1
|
||||
|
||||
serial = response[1..7]
|
||||
challenge = response[8..11]
|
||||
|
||||
# send host connection reply
|
||||
reply = "\x02" + serial.pack('C*') + challenge.pack('C*')
|
||||
response = sysex reply
|
||||
|
||||
# decode host connection confirmation
|
||||
status = response[0]
|
||||
raise ( "expected 03, got " + response.inspect ) if status != 3
|
||||
end
|
||||
|
||||
def hdrlc
|
||||
"\xf0\x00\x00\x66\x10"
|
||||
end
|
||||
|
||||
def hdrlcxt
|
||||
"\xf0\x00\x00\x66\x11"
|
||||
end
|
||||
end
|
||||
61
libs/surfaces/mackie/scripts/parse.rb
Normal file
61
libs/surfaces/mackie/scripts/parse.rb
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#! /usr/bin/ruby
|
||||
# Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
require "rexml/document"
|
||||
file = File.new( ARGV[0] )
|
||||
doc = REXML::Document.new file
|
||||
|
||||
# fetch the node containing the controls
|
||||
controls = XPath.first( doc, 'Session/ControlProtocols/Protocol[@name="Generic MIDI"]/controls' )
|
||||
|
||||
channel = 1
|
||||
|
||||
# A Control is a button or slider. It has an internal ID
|
||||
# an incoming MIDI message, and an outgoing midi message
|
||||
class Control
|
||||
|
||||
end
|
||||
|
||||
# Strips have solo,rec,mute,pan,fader
|
||||
# Strips have midi input
|
||||
# Strips have midi output
|
||||
# Strips have an XML representation, or something like that
|
||||
class Strip
|
||||
def initialize( node )
|
||||
@solo = node.elements['solo']
|
||||
@mute = node.elements['mute']
|
||||
@rec = node.elements['recenable']
|
||||
@fader = node.elements['IO/gaincontrol']
|
||||
@panner = node.elements['IO/Panner/StreamPanner/panner']
|
||||
end
|
||||
end
|
||||
|
||||
# This knows how to extract a set of controls from a Route
|
||||
|
||||
doc.elements.each( 'Session/Routes/Route' ) do |node|
|
||||
strip = Strip.new( node )
|
||||
|
||||
controls.add_element( 'mute',
|
||||
'id' => mute.attribute('id').value,
|
||||
'event' => "0xb0",
|
||||
'channel' => channel.to_s,
|
||||
'additional' => "0x41"
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
pp controls.elements
|
||||
137
libs/surfaces/mackie/scripts/signals.rb
Normal file
137
libs/surfaces/mackie/scripts/signals.rb
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#~ /usr/bin/ruby
|
||||
# Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
require 'erb'
|
||||
|
||||
signals = %w{
|
||||
solo_changed
|
||||
mute_changed
|
||||
record_enable_changed
|
||||
gain_changed
|
||||
name_changed
|
||||
panner_changed
|
||||
}
|
||||
|
||||
@signal_calls = { 'panner_changed' => 'panner()[0]->Changed' }
|
||||
|
||||
def connection_call( x )
|
||||
if @signal_calls.include? x
|
||||
@signal_calls[x]
|
||||
else
|
||||
x
|
||||
end
|
||||
end
|
||||
|
||||
signals.each do |x|
|
||||
puts <<EOF
|
||||
void MackieControlProtocol::notify_#{x}( void *, ARDOUR::Route * route )
|
||||
{
|
||||
try
|
||||
{
|
||||
strip_from_route( route ).#{x.gsub( /_changed/, '' )}();
|
||||
}
|
||||
catch( exception & e )
|
||||
{
|
||||
cout << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
end
|
||||
|
||||
class_def = <<EOF
|
||||
#ifndef route_signal_h
|
||||
#define route_signal_h
|
||||
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
class MackieControlProtocol;
|
||||
|
||||
namespace ARDOUR {
|
||||
class Route;
|
||||
}
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class Strip;
|
||||
|
||||
/**
|
||||
This class is intended to easily create and destroy the set of
|
||||
connections from a route to a control surface strip. Instanting
|
||||
it will connect the signals, and destructing it will disconnect
|
||||
the signals.
|
||||
*/
|
||||
class RouteSignal
|
||||
{
|
||||
public:
|
||||
RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip )
|
||||
: _route( route ), _mcp( mcp ), _strip( strip )
|
||||
{
|
||||
connect();
|
||||
}
|
||||
|
||||
~RouteSignal()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
private:
|
||||
ARDOUR::Route & _route;
|
||||
MackieControlProtocol & _mcp;
|
||||
Strip & _strip;
|
||||
|
||||
<% signals.each do |x| -%>
|
||||
sigc::connection _<%= x %>_connection;
|
||||
<% end -%>
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
EOF
|
||||
|
||||
erb = ERB.new( class_def, 0, ">-" )
|
||||
erb.run
|
||||
|
||||
impl_def = <<EOF
|
||||
#include "route_signal.h"
|
||||
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/panner.h>
|
||||
|
||||
#include "mackie_control_protocol.h"
|
||||
|
||||
using namespace Mackie;
|
||||
|
||||
void RouteSignal::connect()
|
||||
{
|
||||
<% signals.each do |x| -%>
|
||||
_<%=x%>_connection = _route.<%=connection_call(x)%>.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_<%=x%> ), &_route ) );
|
||||
<% end -%>
|
||||
}
|
||||
|
||||
void RouteSignal::disconnect()
|
||||
{
|
||||
<% signals.each do |x| -%>
|
||||
_<%= x %>_connection.disconnect();
|
||||
<% end -%>
|
||||
}
|
||||
EOF
|
||||
|
||||
erb = ERB.new( impl_def, 0, ">-" )
|
||||
erb.run
|
||||
137
libs/surfaces/mackie/scripts/simple_host.rb
Normal file
137
libs/surfaces/mackie/scripts/simple_host.rb
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#! /usr/bin/ruby
|
||||
# Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
require 'mackie'
|
||||
|
||||
buttons = {}
|
||||
pots = {}
|
||||
|
||||
while !File.exist? ARGV[0]
|
||||
sleep 0.010
|
||||
end
|
||||
|
||||
file = File.open( ARGV[0], 'r+' )
|
||||
mck = Mackie.new( file )
|
||||
|
||||
# faders to minimum. bcf2000 doesn't respond
|
||||
mck.write_sysex "\x61"
|
||||
|
||||
# all leds off. bcf2000 doesn't respond
|
||||
mck.write_sysex "\x62"
|
||||
|
||||
# get version. comes back as ASCII bytes
|
||||
version = mck.sysex "\x13\x00"
|
||||
puts "version: #{version.map{|x| x.chr}}"
|
||||
|
||||
# respond to control movements
|
||||
while bytes = file.read( 3 )
|
||||
puts "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ]
|
||||
output = nil
|
||||
case bytes[0] & 0b11110000
|
||||
when 0xe0
|
||||
# fader moved, so respond if move is OK
|
||||
output = bytes
|
||||
when 0x90
|
||||
# button pressed
|
||||
case bytes[1]
|
||||
when 0x68..0x6f
|
||||
# do nothing - touch detection
|
||||
puts "touch detect: %02.x" % bytes[2]
|
||||
else
|
||||
# treat all buttons as toggles
|
||||
button_id = bytes[1]
|
||||
|
||||
# only toggle on release. Not working. All buttons send press
|
||||
# and then release signals
|
||||
if bytes[2] == 0
|
||||
if buttons.include?( button_id )
|
||||
# toggle button state
|
||||
puts "button id #{buttons[button_id]} to #{!buttons[button_id]}"
|
||||
buttons[button_id] = !buttons[button_id]
|
||||
else
|
||||
# create a new button as on
|
||||
puts "adding button id #{button_id}"
|
||||
buttons[button_id] = true
|
||||
end
|
||||
bytes[2] = buttons[button_id] ? 0x7f : 0
|
||||
output = bytes
|
||||
end
|
||||
end
|
||||
when 0xb0
|
||||
# pots, jog wheel, external
|
||||
case bytes[1]
|
||||
when 0x10..0x17
|
||||
#pot turned
|
||||
pot_id = bytes[1] & 0b00000111
|
||||
direction = bytes[2] & 0b01000000
|
||||
delta = bytes[2] & 0b00111111
|
||||
sign = direction == 0 ? 1 : -1
|
||||
|
||||
if pots.include? pot_id
|
||||
current_led_pos = pots[pot_id]
|
||||
else
|
||||
current_led_pos = pots[pot_id] = 6
|
||||
end
|
||||
new_led_pos = current_led_pos + sign
|
||||
new_led_pos = case
|
||||
when new_led_pos <= 0
|
||||
0
|
||||
when new_led_pos >= 11
|
||||
11
|
||||
else
|
||||
new_led_pos
|
||||
end
|
||||
|
||||
pots[pot_id] = new_led_pos
|
||||
|
||||
puts "pot #{pot_id} turned #{sign} #{direction == 0 ? 'clockwise' : 'widdershins'}: %02.x to #{new_led_pos}" % delta
|
||||
|
||||
output = bytes
|
||||
output[1] += 0x20
|
||||
output[2] = 0b01000000
|
||||
#~ modes:
|
||||
#~ 0 - single dot
|
||||
#~ 1 - boost/cut
|
||||
#~ 2 - wrap
|
||||
#~ 3 - spread
|
||||
mode = pot_id < 4 ? pot_id : 0
|
||||
output[2] |= ( mode << 4 )
|
||||
output[2] += ( new_led_pos ) & 0b00001111
|
||||
when 0x2e
|
||||
# external controller
|
||||
when 0x3c
|
||||
# jog wheel
|
||||
end
|
||||
else
|
||||
puts "don't know what this means"
|
||||
end
|
||||
|
||||
# output bytes
|
||||
if output
|
||||
#sleep 0.1
|
||||
puts "sending: %02.x %02.x %02.x" % [ output[0], output[1], output[2] ]
|
||||
begin
|
||||
res = file.write output
|
||||
puts "res: #{res}"
|
||||
file.flush
|
||||
rescue => e
|
||||
puts "oops #{e}"
|
||||
file.close
|
||||
file = File.open ARGV[0], 'r+'
|
||||
end
|
||||
end
|
||||
end
|
||||
95
libs/surfaces/mackie/scripts/surface-cc-template.erb
Normal file
95
libs/surfaces/mackie/scripts/surface-cc-template.erb
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<%#
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
-%>
|
||||
/*
|
||||
Generated by scripts/generate-surface.rb
|
||||
*/
|
||||
|
||||
#include "<%= sf.name.downcase %>_surface.h"
|
||||
|
||||
#include "controls.h"
|
||||
#include "mackie_button_handler.h"
|
||||
|
||||
using namespace Mackie;
|
||||
|
||||
void Mackie::<%= sf.name %>Surface::init_controls()
|
||||
{
|
||||
// intialise groups and strips
|
||||
Group * group = 0;
|
||||
|
||||
// make sure there are enough strips
|
||||
strips.resize( <%= sf.groups.values.find_all{|x| x.name =~ /strip/}.size %> );
|
||||
|
||||
% sf.groups.values.each do |group|
|
||||
<%- if group.class == Strip -%>
|
||||
<%- if group.name == 'master' -%>
|
||||
group = new MasterStrip ( "<%=group.name%>", 0 );
|
||||
<%- else -%>
|
||||
group = new <%= group.class.name %> ( "<%=group.name%>", <%=group.ordinal - 1%> );
|
||||
<%- end -%>
|
||||
<%- else -%>
|
||||
group = new <%= group.class.name %> ( "<%=group.name%>" );
|
||||
<%- end -%>
|
||||
groups["<%=group.name%>"] = group;
|
||||
<%- if group.class == Strip -%>
|
||||
strips[<%=group.ordinal - 1%>] = dynamic_cast<Strip*>( group );
|
||||
<%- end -%>
|
||||
|
||||
% end
|
||||
|
||||
// initialise controls
|
||||
Control * control = 0;
|
||||
|
||||
% sf.controls.each do |control|
|
||||
group = groups["<%=control.group.name%>"];
|
||||
control = new <%= control.class.name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group );
|
||||
<%=control.class.name.downcase%>s[0x<%=control.id.to_hex %>] = control;
|
||||
controls.push_back( control );
|
||||
<%- if control.group.class != Strip -%>
|
||||
controls_by_name["<%= control.name %>"] = control;
|
||||
<%- end -%>
|
||||
group->add( *control );
|
||||
|
||||
% end
|
||||
}
|
||||
|
||||
void Mackie::<%= sf.name %>Surface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
|
||||
{
|
||||
if ( bs != press && bs != release )
|
||||
{
|
||||
mbh.update_led( button, none );
|
||||
return;
|
||||
}
|
||||
|
||||
LedState ls;
|
||||
switch ( button.id() )
|
||||
{
|
||||
<%-
|
||||
buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
|
||||
buttons.each do |button|
|
||||
%>
|
||||
case 0x<%= button.id.to_hex %>: // <%= button.name %>
|
||||
switch ( bs ) {
|
||||
case press: ls = mbh.<%= button.name %>_press( button ); break;
|
||||
case release: ls = mbh.<%= button.name %>_release( button ); break;
|
||||
case neither: break;
|
||||
}
|
||||
break;
|
||||
<% end %>
|
||||
}
|
||||
mbh.update_led( button, ls );
|
||||
}
|
||||
27
libs/surfaces/mackie/scripts/surface-h-template.erb
Normal file
27
libs/surfaces/mackie/scripts/surface-h-template.erb
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef mackie_surface_<%= sf.name.downcase %>_h
|
||||
#define mackie_surface_<%= sf.name.downcase %>_h
|
||||
/*
|
||||
Generated by scripts/generate-surface.rb
|
||||
*/
|
||||
|
||||
#include "surface.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler;
|
||||
|
||||
class <%= sf.name %>Surface : public Surface
|
||||
{
|
||||
public:
|
||||
<%= sf.name %>Surface( uint32_t max_strips ) : Surface( max_strips )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
|
||||
virtual void init_controls();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
9
libs/surfaces/mackie/scripts/test_controls.rb
Executable file
9
libs/surfaces/mackie/scripts/test_controls.rb
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#! /usr/bin/ruby
|
||||
|
||||
require 'controls.rb'
|
||||
require 'pp'
|
||||
|
||||
sf = Surface.new
|
||||
sf.parse
|
||||
sf.types.each{|k,v| puts "%02.x #{v}" % k}
|
||||
|
||||
26
libs/surfaces/mackie/scripts/transform.rb
Normal file
26
libs/surfaces/mackie/scripts/transform.rb
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
class ElementHandler
|
||||
|
||||
def apply( anElement )
|
||||
anElement.each {|e| handle(e)} if anElement
|
||||
end
|
||||
|
||||
def handle( aNode )
|
||||
if aNode.kind_of? REXML::Text
|
||||
handleTextNode(aNode)
|
||||
elsif aNode.kind_of? REXML::Element
|
||||
handle_element aNode
|
||||
else
|
||||
return #ignore comments and processing instructions
|
||||
end
|
||||
end
|
||||
|
||||
def handle_element( anElement )
|
||||
handler_method = "handle_" + anElement.name.tr("-","_")
|
||||
if self.respond_to? handler_method
|
||||
self.send(handler_method, anElement)
|
||||
else
|
||||
default_handler(anElement)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
10
libs/surfaces/mackie/scripts/write.rb
Normal file
10
libs/surfaces/mackie/scripts/write.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#! /usr/bin/ruby
|
||||
|
||||
require 'mackie.rb'
|
||||
|
||||
@file = File.open '/dev/snd/midiC2D0', 'r+'
|
||||
|
||||
@led_8_on = [ 0x90, 0x18, 0x7f ]
|
||||
@hci = [ 0, 0xf7 ]
|
||||
@version_req = [ 0x13, 0, 0xf7 ]
|
||||
|
||||
145
libs/surfaces/mackie/surface.cc
Normal file
145
libs/surfaces/mackie/surface.cc
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#include "surface.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
|
||||
Surface::Surface( uint32_t max_strips )
|
||||
: _max_strips( max_strips )
|
||||
{
|
||||
}
|
||||
|
||||
void Surface::init()
|
||||
{
|
||||
init_controls();
|
||||
// TODO fix this hard coding
|
||||
init_strips( _max_strips, 8 );
|
||||
}
|
||||
|
||||
Surface::~Surface()
|
||||
{
|
||||
// delete groups
|
||||
for( Groups::iterator it = groups.begin(); it != groups.end(); ++it )
|
||||
{
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
// delete controls
|
||||
for( Controls::iterator it = controls.begin(); it != controls.end(); ++it )
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly Mackie-specific, because of multiple devices on separate ports
|
||||
// add the strips from 9..max_strips
|
||||
// unit_strips is the number of strips for additional units.
|
||||
// TODO would be better to pass the MackiePorts in here, cos
|
||||
// each might have a different number of strips
|
||||
void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips )
|
||||
{
|
||||
if ( strips.size() < max_strips )
|
||||
{
|
||||
strips.resize( max_strips );
|
||||
for ( uint32_t i = strips.size(); i < max_strips; ++i )
|
||||
{
|
||||
// because I can't find itoa
|
||||
ostringstream os;
|
||||
os << "strip_" << i + 1;
|
||||
string name = os.str();
|
||||
|
||||
// shallow copy existing strip
|
||||
// which works because the controls
|
||||
// have the same ids across units
|
||||
Strip * strip = new Strip( *strips[i % unit_strips] );
|
||||
|
||||
// update the relevant values
|
||||
strip->index( i );
|
||||
strip->name( name );
|
||||
|
||||
// add to data structures
|
||||
groups[name] = strip;
|
||||
strips[i] = strip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ostream & Mackie::operator << ( ostream & os, const Mackie::Control & control )
|
||||
{
|
||||
os << typeid( control ).name();
|
||||
os << " { ";
|
||||
os << "name: " << control.name();
|
||||
os << ", ";
|
||||
os << "id: " << "0x" << setw(2) << setfill('0') << hex << control.id() << setfill(' ');
|
||||
os << ", ";
|
||||
os << "ordinal: " << dec << control.ordinal();
|
||||
os << ", ";
|
||||
os << "group: " << control.group().name();
|
||||
os << " }";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
TODO could optimise this to use enum, but it's only
|
||||
called during the protocol class instantiation.
|
||||
|
||||
generated using
|
||||
|
||||
irb -r controls.rb
|
||||
sf=Surface.new
|
||||
sf.parse
|
||||
controls = sf.groups.find{|x| x[0] =~ /strip/}.each{|x| puts x[1]}
|
||||
controls[1].each {|x| puts "\telse if ( control.name() == \"#{x.name}\" )\n\t{\n\t\t_#{x.name} = reinterpret_cast<#{x.class.name}*>(&control);\n\t}\n"}
|
||||
*/
|
||||
void Strip::add( Control & control )
|
||||
{
|
||||
Group::add( control );
|
||||
if ( control.name() == "gain" )
|
||||
{
|
||||
_gain = reinterpret_cast<Fader*>(&control);
|
||||
}
|
||||
else if ( control.name() == "vpot" )
|
||||
{
|
||||
_vpot = reinterpret_cast<Pot*>(&control);
|
||||
}
|
||||
else if ( control.name() == "recenable" )
|
||||
{
|
||||
_recenable = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "solo" )
|
||||
{
|
||||
_solo = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "mute" )
|
||||
{
|
||||
_mute = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "select" )
|
||||
{
|
||||
_select = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "vselect" )
|
||||
{
|
||||
_vselect = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "fader_touch" )
|
||||
{
|
||||
_fader_touch = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.type() == Control::type_led || control.type() == Control::type_led_ring )
|
||||
{
|
||||
// do nothing
|
||||
cout << "Strip::add not adding " << control << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ostringstream os;
|
||||
os << "Strip::add: unknown control type " << control;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
}
|
||||
|
||||
85
libs/surfaces/mackie/surface.h
Normal file
85
libs/surfaces/mackie/surface.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#ifndef mackie_surface_h
|
||||
#define mackie_surface_h
|
||||
|
||||
#include "controls.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler;
|
||||
|
||||
/**
|
||||
This represents an entire control surface, made up of Groups,
|
||||
Strips and Controls. There are several collections for
|
||||
ease of addressing in different ways, but only one collection
|
||||
has definitive ownership.
|
||||
|
||||
It handles mapping button ids to press_ and release_ calls.
|
||||
|
||||
There are various emulations of the Mackie around, so specific
|
||||
emulations will inherit from this to change button mapping, or
|
||||
have 7 fader channels instead of 8, or whatever.
|
||||
|
||||
Currently there are BcfSurface and MackieSurface.
|
||||
|
||||
TODO maybe make Group inherit from Control, for ease of ownership.
|
||||
*/
|
||||
class Surface
|
||||
{
|
||||
public:
|
||||
Surface( uint32_t max_strips );
|
||||
virtual ~Surface();
|
||||
|
||||
/// Calls the virtual initialisation methods. This *must* be called after
|
||||
/// construction, because c++ is too dumb to call virtual methods from
|
||||
/// inside a constructor
|
||||
void init();
|
||||
|
||||
typedef std::vector<Control*> Controls;
|
||||
|
||||
/// This collection has ownership of all the controls
|
||||
Controls controls;
|
||||
|
||||
/**
|
||||
These are alternative addressing schemes
|
||||
They use maps because the indices aren't always
|
||||
0-based.
|
||||
*/
|
||||
std::map<int,Control*> faders;
|
||||
std::map<int,Control*> pots;
|
||||
std::map<int,Control*> buttons;
|
||||
std::map<int,Control*> leds;
|
||||
|
||||
/// no strip controls in here because they usually
|
||||
/// have the same names.
|
||||
std::map<std::string,Control*> controls_by_name;
|
||||
|
||||
/// The collection of all numbered strips. No master
|
||||
/// strip in here.
|
||||
typedef std::vector<Strip*> Strips;
|
||||
Strips strips;
|
||||
|
||||
/// This collection owns the groups
|
||||
typedef std::map<std::string,Group*> Groups;
|
||||
Groups groups;
|
||||
|
||||
uint32_t max_strips() const
|
||||
{
|
||||
return _max_strips;
|
||||
}
|
||||
|
||||
/// map button ids to calls to press_ and release_ in mbh
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) = 0;
|
||||
|
||||
protected:
|
||||
virtual void init_controls() = 0;
|
||||
virtual void init_strips( uint32_t max_strips, uint32_t unit_strips );
|
||||
|
||||
private:
|
||||
uint32_t _max_strips;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
123
libs/surfaces/mackie/surface_port.cc
Normal file
123
libs/surfaces/mackie/surface_port.cc
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include "surface_port.h"
|
||||
|
||||
#include "mackie_control_exception.h"
|
||||
#include "controls.h"
|
||||
|
||||
#include <midi++/types.h>
|
||||
#include <midi++/port.h>
|
||||
#include <sigc++/sigc++.h>
|
||||
#include <boost/shared_array.hpp>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
|
||||
SurfacePort::SurfacePort( MIDI::Port & port, int number )
|
||||
: _port( port ), _number( number ), _active( false )
|
||||
{
|
||||
}
|
||||
|
||||
MidiByteArray SurfacePort::read()
|
||||
{
|
||||
const int max_buf_size = 512;
|
||||
MIDI::byte buf[max_buf_size];
|
||||
MidiByteArray retval;
|
||||
|
||||
int nread = port().read( buf, sizeof (buf) );
|
||||
|
||||
if (nread >= 0) {
|
||||
retval.copy( nread, buf );
|
||||
if ((size_t) nread == sizeof (buf))
|
||||
{
|
||||
retval << read();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ostringstream os;
|
||||
os << "error reading from port: " << port().name() << " nread: " << nread;
|
||||
cout << os.str() << endl;
|
||||
inactive_event();
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void SurfacePort::write( const MidiByteArray & mba )
|
||||
{
|
||||
if ( mba[0] == 0xf0 ) cout << "SurfacePort::write: " << mba << endl;
|
||||
//cout << "SurfacePort::write: " << mba << endl;
|
||||
int count = port().write( mba.bytes().get(), mba.size() );
|
||||
if ( count != (int)mba.size() )
|
||||
{
|
||||
inactive_event();
|
||||
ostringstream os;
|
||||
os << _("Surface: couldn't write to port ") << port().name();
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
//if ( mba[0] == 0xf0 ) cout << "SurfacePort::write " << count << endl;
|
||||
}
|
||||
|
||||
void SurfacePort::write_sysex( const MidiByteArray & mba )
|
||||
{
|
||||
MidiByteArray buf;
|
||||
buf << sysex_hdr() << mba << MIDI::eox;
|
||||
write( buf );
|
||||
}
|
||||
|
||||
void SurfacePort::write_sysex( MIDI::byte msg )
|
||||
{
|
||||
MidiByteArray buf;
|
||||
buf << sysex_hdr() << msg << MIDI::eox;
|
||||
write( buf );
|
||||
}
|
||||
|
||||
// This should be moved to midi++ at some point
|
||||
ostream & operator << ( ostream & os, const MIDI::Port & port )
|
||||
{
|
||||
os << "device: " << port.device();
|
||||
os << "; ";
|
||||
os << "name: " << port.name();
|
||||
os << "; ";
|
||||
os << "type: " << port.type();
|
||||
os << "; ";
|
||||
os << "mode: " << port.mode();
|
||||
os << "; ";
|
||||
os << "ok: " << port.ok();
|
||||
os << "; ";
|
||||
os << "number: " << port.number();
|
||||
os << "; ";
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
|
||||
{
|
||||
os << "{ ";
|
||||
os << "device: " << port.port().device();
|
||||
os << "; ";
|
||||
os << "name: " << port.port().name();
|
||||
os << "; ";
|
||||
os << "number: " << port.number();
|
||||
os << " }";
|
||||
return os;
|
||||
}
|
||||
95
libs/surfaces/mackie/surface_port.h
Normal file
95
libs/surfaces/mackie/surface_port.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef surface_port_h
|
||||
#define surface_port_h
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
}
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
/**
|
||||
Make a relationship between a midi port and a Mackie device.
|
||||
*/
|
||||
class SurfacePort : public sigc::trackable
|
||||
{
|
||||
public:
|
||||
SurfacePort( MIDI::Port & port, int number );
|
||||
virtual ~SurfacePort() {}
|
||||
|
||||
// when this is successful, active() should return true
|
||||
virtual void open() = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
/// read bytes from the port. They'll either end up in the
|
||||
/// parser, or if that's not active they'll be returned
|
||||
MidiByteArray read();
|
||||
|
||||
/// an easier way to output bytes via midi
|
||||
void write( const MidiByteArray & );
|
||||
|
||||
/// write a sysex message
|
||||
void write_sysex( const MidiByteArray & mba );
|
||||
void write_sysex( MIDI::byte msg );
|
||||
|
||||
// return the correct sysex header for this port
|
||||
virtual const MidiByteArray & sysex_hdr() const = 0;
|
||||
|
||||
MIDI::Port & port() { return _port; }
|
||||
const MIDI::Port & port() const { return _port; }
|
||||
|
||||
// all control notofications are sent from here
|
||||
sigc::signal<void, SurfacePort &, Control &, const ControlState &> control_event;
|
||||
|
||||
// emitted just before the port goes into initialisation
|
||||
// where it tries to establish that its device is connected
|
||||
sigc::signal<void> init_event;
|
||||
|
||||
// emitted when the port completes initialisation successfully
|
||||
sigc::signal<void> active_event;
|
||||
|
||||
// emitted when the port goes inactive (ie a read or write failed)
|
||||
sigc::signal<void> inactive_event;
|
||||
|
||||
// the port number - master is 0, extenders are 1,2,3,4
|
||||
virtual int number() const { return _number; }
|
||||
|
||||
// number of strips handled by this port. Usually 8.
|
||||
virtual int strips() const = 0;
|
||||
|
||||
virtual bool active() const { return _active; }
|
||||
virtual void active( bool yn ) { _active = yn; }
|
||||
|
||||
private:
|
||||
MIDI::Port & _port;
|
||||
int _number;
|
||||
bool _active;
|
||||
};
|
||||
|
||||
std::ostream & operator << ( std::ostream & , const SurfacePort & port );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
25
libs/surfaces/mackie/test.cc
Normal file
25
libs/surfaces/mackie/test.cc
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace MIDI {
|
||||
typedef unsigned char byte;
|
||||
byte sysex = 0xf0;
|
||||
byte eox = 0xf7;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
MidiByteArray bytes( 4, 0xf0, 0x01, 0x03, 0x7f );
|
||||
cout << bytes << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
9
libs/surfaces/mackie/types.cc
Normal file
9
libs/surfaces/mackie/types.cc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#include "types.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
LedState on( LedState::on );
|
||||
LedState off( LedState::off );
|
||||
LedState flashing( LedState::flashing );
|
||||
LedState none( LedState::none );
|
||||
}
|
||||
93
libs/surfaces/mackie/types.h
Normal file
93
libs/surfaces/mackie/types.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright (C) 2006,2007 John Anderson
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef mackie_types_h
|
||||
#define mackie_types_h
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
/**
|
||||
This started off as an enum, but it got really annoying
|
||||
typing ? on : off
|
||||
*/
|
||||
class LedState
|
||||
{
|
||||
public:
|
||||
enum state_t { none, off, flashing, on };
|
||||
LedState() : _state( none ) {}
|
||||
LedState( bool yn ): _state( yn ? on : off ) {}
|
||||
LedState( state_t state ): _state( state ) {}
|
||||
|
||||
bool operator == ( const LedState & other ) const
|
||||
{
|
||||
return state() == other.state();
|
||||
}
|
||||
|
||||
bool operator != ( const LedState & other ) const
|
||||
{
|
||||
return state() != other.state();
|
||||
}
|
||||
|
||||
state_t state() const { return _state; }
|
||||
|
||||
private:
|
||||
state_t _state;
|
||||
};
|
||||
|
||||
extern LedState on;
|
||||
extern LedState off;
|
||||
extern LedState flashing;
|
||||
extern LedState none;
|
||||
|
||||
enum ButtonState { neither = -1, release = 0, press = 1 };
|
||||
|
||||
/**
|
||||
Contains the state for a control, with some convenience
|
||||
constructors
|
||||
*/
|
||||
struct ControlState
|
||||
{
|
||||
ControlState(): pos(0.0), delta(0.0), button_state(neither) {}
|
||||
|
||||
ControlState( LedState ls ): pos(0.0), delta(0.0), led_state(ls), button_state(neither) {}
|
||||
|
||||
// Note that this sets both pos and delta to the flt value
|
||||
ControlState( LedState ls, float flt ): pos(flt), delta(flt), ticks(0), led_state(ls), button_state(neither) {}
|
||||
ControlState( float flt ): pos(flt), delta(flt), ticks(0), led_state(none), button_state(neither) {}
|
||||
ControlState( float flt, int tcks ): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {}
|
||||
ControlState( ButtonState bs ): pos(0.0), delta(0.0), ticks(0), led_state(none), button_state(bs) {}
|
||||
|
||||
float pos;
|
||||
float delta;
|
||||
int ticks;
|
||||
LedState led_state;
|
||||
ButtonState button_state;
|
||||
};
|
||||
|
||||
class Control;
|
||||
class Fader;
|
||||
class Button;
|
||||
class Strip;
|
||||
class Group;
|
||||
class Pot;
|
||||
class Led;
|
||||
class LedRing;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
90
libs/surfaces/tranzport/README
Normal file
90
libs/surfaces/tranzport/README
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
I'm putting this here because I don't really have a place to put it, unless I create a web page and have a place to keep the code.
|
||||
|
||||
While doing some exaustive testing of my latest code (read - playing a ton of music) I have done some thinking about the ui, and decided that under apparent simplicity should lie complexity.
|
||||
|
||||
The "flash" screen idea I am going to drop, and replace it with the idea of a notify area being declared on each screen.
|
||||
|
||||
When the unit is idle, these messages will appear in that area statically. When the transport is running, these messages will still appear in that space, which is usually where the meter is. It's actually possible to rapidly flash the area between the competing writers and get a nifty faded effect. (I came across this idea accidentally when I had a pointer overrun)
|
||||
|
||||
Also certain things will update on top of each other, whatever was updated
|
||||
last will stay on the screen. This is Pan/Gain primarily.
|
||||
|
||||
I need a way to get messages back into the tranzport. Example - I hit Undo, what was undone? Redo, same problem.
|
||||
|
||||
I've already found many uses for being able to control more than 1 track at a time, so I think that although I'm *usually* controlling one track at a time, being able to quickly access all tracks would be good.
|
||||
|
||||
Example - want to have meters for all tracks running and be able to control the
|
||||
db/pan settings of the track I'm on....
|
||||
|
||||
What I am going to go with is multifold - but first my design goal: I want to do everything required for a solo musician wielding an instrument to NOT have to touch a keyboard or look at a big screen. When you are wrapped behind a bass, it's difficult to cope with that - but the tranzport is a great alternative.
|
||||
|
||||
Most screens will have *4* items on them.
|
||||
|
||||
There will be "display views" - which are more informational and bling oriented.
|
||||
|
||||
There will be "Track based views"- which basically do track specific things
|
||||
|
||||
There will be "interactive views",which basically allow for more input than output. They will do something to highlight the current selection.
|
||||
Scroll wheel will select between values for a field. Track Next/Prev will move between fields. I would like to have a "No/Yes" option (hit record for yes? Stop for no?) but I'm still a little vague on that.
|
||||
|
||||
Things to do:
|
||||
|
||||
A) Hitting "Shift->Spacebar" will switch "views". There are ultimately going to be dozens of views. At present, there are only the "Normal" and "Big Meter" views.
|
||||
|
||||
Each view will change somewhat based on the state of the transport. Holding down shift for a second will switch to the "underlying display"....
|
||||
|
||||
Here's how the "normal" view looks today in my tree:
|
||||
|
||||
VIEW: NORMAL
|
||||
|
||||
Play Mode: Stopped
|
||||
[Trackname[16]] [gain/pan[4]]
|
||||
[Modes[9]] long smpte/bar counter]
|
||||
|
||||
Play Mode: Playing > 1.0 speed
|
||||
[Trackname[16]] [gain/pan[4]]
|
||||
[meter[16]] short smpte/bar counter]
|
||||
|
||||
Play Mode: Playing < 1.0
|
||||
[Trackname[16]] [gain/pan[4]]
|
||||
[meter[9]] long smpte/bar counter]
|
||||
|
||||
Play Mode: Recording
|
||||
[Trackname[16]] [gain/pan[4]]
|
||||
[meter[16]] short smpte/bar counter]
|
||||
|
||||
Other views (in order of development priority)
|
||||
|
||||
Marker Mode: Edit markers, setup loops and punch in points.
|
||||
Config Mode: Load/Save settings, Load/Save project. Set wheel SnapTo
|
||||
Loop Mode: Show track, raise layers to top for playback, editing, deletion, loop on and off, etc
|
||||
|
||||
It's possible that config mode will have a "MORE" field, or ways to move around the configuration (ffw/Play?)
|
||||
|
||||
(the first two are the two modes I most need personally. If you have a suggestion...)
|
||||
|
||||
Mastering Mode - display master and current track with meters and panner/db
|
||||
Automation Mode - I really don't think I have the pixels for this
|
||||
|
||||
(I've already abstracted out the code to do most of these, but it's bling, I'm not going to bother much with it soon)
|
||||
|
||||
Quad Meter Mode
|
||||
Inverted Meter mode (draws meters backwards)
|
||||
Quad Inverted Meter mode (bling, but my car stereo has it, and it's cool)
|
||||
10 8 bar meter mode
|
||||
5 8 bar meter mode
|
||||
|
||||
I haven't written the panner yet, doing the stereo meter killed me.
|
||||
|
||||
From a development perspective I'm going to keep revising the code to make it more stable and merely tie the new mode modes to the "bling mode"s until they are ready for prime time. I should be able to put out releases once a week for a while.
|
||||
|
||||
A big help would be moving these items into a higher level of abstraction (revising the baseUI class).
|
||||
|
||||
In particular, I'd really like "slave" mode. Snapto increment is really important....
|
||||
|
||||
Here's an example of something that should be fairly easy to export to the Base::UI subclasses - the current state of the main keyboard's shift key.
|
||||
|
||||
That way, when shift is held down for a few seconds on the regular keyboard, I can see what's underneath the current tranzport display mode (I do like big meters) (Also, it's somewhat easier to hit shift on the main keyboard and play on the tranzport or the shuttle wheel, if that's what you are doing).
|
||||
|
||||
Should be fairly easy to tap into the gdk event for this but the "right way" to propagate this event into the class is beyond me.
|
||||
|
|
@ -22,13 +22,36 @@ tranzport.Append(POTFILE = domain + '.pot')
|
|||
|
||||
tranzport_files=Split("""
|
||||
interface.cc
|
||||
tranzport_control_protocol.cc
|
||||
buttons.cc
|
||||
io.cc
|
||||
io_usb.cc
|
||||
panner.cc
|
||||
lights.cc
|
||||
screen.cc
|
||||
state.cc
|
||||
wheel_modes.cc
|
||||
button_events.cc
|
||||
general.cc
|
||||
lcd.cc
|
||||
mode.cc
|
||||
show.cc
|
||||
init.cc
|
||||
wheel.cc
|
||||
""")
|
||||
|
||||
#figure out when to do
|
||||
#io_usb.cc
|
||||
#io_midi.cc
|
||||
#io_kernel.cc
|
||||
|
||||
tranzport.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
|
||||
tranzport.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
|
||||
tranzport.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
|
||||
tranzport.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
|
||||
#if env['HAVE_TRANZPORT_KERNEL_DRIVER']:
|
||||
# tranzport.Append(CXXFLAGS="-DHAVE_TRANZPORT_KERNEL_DRIVER=1")
|
||||
|
||||
#merge more into tranzport files for the right io lib
|
||||
|
||||
tranzport.Merge ([
|
||||
libraries['ardour'],
|
||||
|
|
@ -37,9 +60,9 @@ tranzport.Merge ([
|
|||
libraries['pbd'],
|
||||
libraries['midi++2'],
|
||||
libraries['xml'],
|
||||
libraries['usb'],
|
||||
libraries['glib2'],
|
||||
libraries['glibmm2']
|
||||
libraries['glibmm2'],
|
||||
libraries['usb']
|
||||
])
|
||||
|
||||
libardour_tranzport = tranzport.SharedLibrary('ardour_tranzport', tranzport_files)
|
||||
|
|
@ -48,6 +71,11 @@ if tranzport['TRANZPORT']:
|
|||
Default(libardour_tranzport)
|
||||
if env['NLS']:
|
||||
i18n (tranzport, tranzport_files, env)
|
||||
# if env['HAVE_TRANZPORT_KERNEL_DRIVER']:
|
||||
# tranzport.Merge([
|
||||
# libraries['usb']
|
||||
# ])
|
||||
|
||||
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'surfaces'), libardour_tranzport))
|
||||
|
||||
env.Alias('tarball', env.Distribute (env['DISTTREE'],
|
||||
|
|
|
|||
6
libs/surfaces/tranzport/TODO
Normal file
6
libs/surfaces/tranzport/TODO
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
BREAK APART DRIVER INTO SIMPLER CHUNKS - done
|
||||
GET KERNEL DRIVER WORKING
|
||||
GET TRIPLE THREADED DRIVER WORKING
|
||||
STABLIZE THE API
|
||||
ADOPT SOME CONVENTIONS FROM THE MACKIE
|
||||
GET SAVING STATE WORKING
|
||||
115
libs/surfaces/tranzport/bling.cc
Normal file
115
libs/surfaces/tranzport/bling.cc
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* The Bling class theoretically knows nothing about the device it's blinging
|
||||
and depends on the overlying implementation to tell it about the format of the
|
||||
device. Maybe this will become a template or people will inherit from it */
|
||||
|
||||
/* Bling is where all the bad, bad, marketing driven ideas go */
|
||||
|
||||
class bling {
|
||||
public:
|
||||
enum BlingMode {
|
||||
BlingOff = 0,
|
||||
BlingOn = 1,
|
||||
BlingEnter = 2,
|
||||
BlingExit = 4,
|
||||
// Light Specific Stuff
|
||||
BlingKit = 8,
|
||||
BlingRotating = 16,
|
||||
BlingPairs = 32,
|
||||
BlingRows = 64,
|
||||
BlingColumns = 128,
|
||||
BlingFlashAllLights = 256,
|
||||
// Screen Specific Stuff
|
||||
// Slider Specific Stuff
|
||||
BlingSliderMax,
|
||||
BlingSliderMid,
|
||||
BlingSliderMin,
|
||||
// Random stuff
|
||||
BlingRandomLight,
|
||||
BlingRandomSlider,
|
||||
BlingRandomScreen,
|
||||
BlingAllSliders
|
||||
};
|
||||
bling();
|
||||
~bling();
|
||||
set(BlingMode);
|
||||
unset(BlingMode);
|
||||
run();
|
||||
next();
|
||||
prev();
|
||||
msg(string&);
|
||||
scrollmsg(string&);
|
||||
|
||||
protected:
|
||||
// The as yet undefined "advanced_ui" class provides methods to find out at run time
|
||||
// what the heck is what
|
||||
BlingMode blingmode;
|
||||
advancedUI *intf;
|
||||
int last_light;
|
||||
// I don't think these actually need to be part of the public definition of the class
|
||||
enter();
|
||||
exit();
|
||||
rotate();
|
||||
// etc
|
||||
};
|
||||
|
||||
// make absolutely sure we have the pointer to the interface
|
||||
// something like this
|
||||
|
||||
#define BLING_INTFA(a) (intf)? 0:intf->a
|
||||
#define BLING_INTF(a) { if (intf) { intf->a; } else { return 0; } }
|
||||
|
||||
// Should any of these bother to return a status code?
|
||||
|
||||
bling::rotate() {
|
||||
BLING_INTF(light(last_light,off));
|
||||
last_light = BLING_INTFA(next_light(last_light));
|
||||
BLING_INTF(light(last_light,on));
|
||||
}
|
||||
|
||||
bling::enter() {
|
||||
}
|
||||
|
||||
bling::exit() {
|
||||
}
|
||||
|
||||
bling::flashall() {
|
||||
}
|
||||
|
||||
bling::rows() {
|
||||
}
|
||||
|
||||
bling::columns() {
|
||||
}
|
||||
|
||||
bling::msg() {
|
||||
}
|
||||
|
||||
bling::scrollmsg() {
|
||||
}
|
||||
|
||||
// Based on the current bling mode, do whatever it is you are going to do
|
||||
bling::run() {
|
||||
|
||||
}
|
||||
|
||||
// etc
|
||||
380
libs/surfaces/tranzport/button_events.cc
Normal file
380
libs/surfaces/tranzport/button_events.cc
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <tranzport_common.h>
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
using namespace PBD;
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <pbd/abstract_ui.cc>
|
||||
|
||||
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_battery_press (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_battery_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_backlight_press (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_backlight_release (bool shifted)
|
||||
{
|
||||
#if DEBUG_TRANZPORT
|
||||
printf("backlight released, redrawing (and possibly crashing) display\n");
|
||||
#endif
|
||||
if (shifted) {
|
||||
lcd_damage();
|
||||
lcd_clear();
|
||||
last_where += 1; /* force time redisplay */
|
||||
last_track_gain = FLT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackleft_press (bool shifted)
|
||||
{
|
||||
prev_track ();
|
||||
// not really the right layer for this
|
||||
if(display_mode == DisplayBigMeter) {
|
||||
if (route_table[0] != 0) {
|
||||
notify(route_get_name (0).substr (0, 15).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackleft_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackright_press (bool shifted)
|
||||
{
|
||||
next_track ();
|
||||
// not really the right layer for this
|
||||
if(display_mode == DisplayBigMeter) {
|
||||
if (route_table[0] != 0) {
|
||||
notify(route_get_name (0).substr (0, 15).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackright_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackrec_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
toggle_all_rec_enables ();
|
||||
} else {
|
||||
route_set_rec_enable (0, !route_get_rec_enable (0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackrec_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackmute_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
// Mute ALL? Something useful when a phone call comes in. Mute master?
|
||||
} else {
|
||||
route_set_muted (0, !route_get_muted (0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_trackmute_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
|
||||
{
|
||||
#if DEBUG_TRANZPORT
|
||||
printf("solo pressed\n");
|
||||
#endif
|
||||
if (display_mode == DisplayBigMeter) {
|
||||
light_off (LightAnysolo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shifted) {
|
||||
session->set_all_solo (!session->soloing());
|
||||
} else {
|
||||
route_set_soloed (0, !route_get_soloed (0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_tracksolo_release (bool shifted)
|
||||
{
|
||||
#if DEBUG_TRANZPORT
|
||||
printf("solo released\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_undo_press (bool shifted)
|
||||
{
|
||||
// undohistory->get_state(1);
|
||||
//XMLNode&
|
||||
//UndoHistory::get_state (uint32_t depth)
|
||||
|
||||
if (shifted) {
|
||||
redo (); // someday flash the screen with what was redone
|
||||
notify("Redone!!");
|
||||
} else {
|
||||
undo (); // someday flash the screen with what was undone
|
||||
notify("Undone!!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_undo_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_in_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
toggle_punch_in ();
|
||||
} else {
|
||||
ControlProtocol::ZoomIn (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_in_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_out_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
toggle_punch_out ();
|
||||
} else {
|
||||
ControlProtocol::ZoomOut (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_out_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_punch_press (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_punch_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_loop_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
next_wheel_shift_mode ();
|
||||
} else {
|
||||
loop_toggle ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_loop_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_prev_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
ControlProtocol::ZoomToSession (); /* EMIT SIGNAL */
|
||||
} else {
|
||||
prev_marker ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_prev_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
// Note - add_marker should adhere to the snap to setting
|
||||
// maybe session->audible_frame does that
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_add_press (bool shifted)
|
||||
{
|
||||
add_marker ();
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_add_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_next_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
next_wheel_mode ();
|
||||
} else {
|
||||
next_marker ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_next_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_rewind_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
goto_start ();
|
||||
} else {
|
||||
rewind ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_rewind_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_fastforward_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
goto_end ();
|
||||
} else {
|
||||
ffwd ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_fastforward_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_stop_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
next_display_mode ();
|
||||
} else {
|
||||
transport_stop ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_stop_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_play_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
set_transport_speed (1.0f);
|
||||
} else {
|
||||
transport_play ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_play_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_record_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
save_state ();
|
||||
} else {
|
||||
rec_enable_toggle ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_record_release (bool shifted)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_footswitch_press (bool shifted)
|
||||
{
|
||||
if (shifted) {
|
||||
next_marker (); // think this through, we could also do punch in
|
||||
} else {
|
||||
prev_marker (); // think this through, we could also do punch in
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::button_event_footswitch_release (bool shifted)
|
||||
{
|
||||
if(session->transport_speed() == 0.0)
|
||||
{
|
||||
transport_play ();
|
||||
}
|
||||
}
|
||||
|
||||
// Possible new api example
|
||||
// tries harder to do the right thing if we somehow missed a button down event
|
||||
// which currently happens... a lot.
|
||||
|
||||
void button_event_mute (bool pressed, bool shifted)
|
||||
{
|
||||
static int was_pressed = 0;
|
||||
if((!pressed && !was_pressed) || pressed) {
|
||||
was_pressed = 1;
|
||||
}
|
||||
|
||||
was_pressed = 0;
|
||||
}
|
||||
21
libs/surfaces/tranzport/button_yn.cc
Normal file
21
libs/surfaces/tranzport/button_yn.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* placeholder for button definitions for user edits like yes/no */
|
||||
113
libs/surfaces/tranzport/buttons.cc
Normal file
113
libs/surfaces/tranzport/buttons.cc
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include "tranzport_control_protocol.h"
|
||||
|
||||
#define TRANZPORT_BUTTON_HANDLER(callback, button_arg) if (button_changes & button_arg) { \
|
||||
if (buttonmask & button_arg) { \
|
||||
callback##_press (buttonmask&ButtonShift); } else { callback##_release (buttonmask&ButtonShift); } }
|
||||
|
||||
int
|
||||
TranzportControlProtocol::process (uint8_t* buf)
|
||||
{
|
||||
|
||||
uint32_t this_button_mask;
|
||||
uint32_t button_changes;
|
||||
|
||||
_device_status = buf[1];
|
||||
|
||||
#if DEBUG_TRANZPORT > 10
|
||||
// Perhaps the device can go offline due to flow control, print command bits to see if we have anything interesting
|
||||
if(_device_status == STATUS_ONLINE) {
|
||||
printf("ONLINE : %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
|
||||
}
|
||||
if(_device_status == STATUS_OFFLINE) {
|
||||
printf("OFFLINE : %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
|
||||
}
|
||||
|
||||
if(_device_status != STATUS_OK) { return 1; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
this_button_mask = 0;
|
||||
this_button_mask |= buf[2] << 24;
|
||||
this_button_mask |= buf[3] << 16;
|
||||
this_button_mask |= buf[4] << 8;
|
||||
this_button_mask |= buf[5];
|
||||
_datawheel = buf[6];
|
||||
|
||||
#if DEBUG_TRANZPORT_STATE > 1
|
||||
// Is the state machine incomplete?
|
||||
const unsigned int knownstates = 0x00004000|0x00008000|
|
||||
0x04000000| 0x40000000| 0x00040000| 0x00400000|
|
||||
0x00000400| 0x80000000| 0x02000000| 0x20000000|
|
||||
0x00800000| 0x00080000| 0x00020000| 0x00200000|
|
||||
0x00000200| 0x01000000| 0x10000000| 0x00010000|
|
||||
0x00100000| 0x00000100| 0x08000000| 0x00001000;
|
||||
|
||||
std::bitset<32> bi(knownstates);
|
||||
std::bitset<32> vi(this_button_mask);
|
||||
|
||||
// if an bi & vi == vi the same - it's a valid set
|
||||
|
||||
if(vi != (bi & vi)) {
|
||||
printf("UNKNOWN STATE: %s also, datawheel= %d\n", vi.to_string().c_str(), _datawheel);
|
||||
}
|
||||
#endif
|
||||
|
||||
button_changes = (this_button_mask ^ buttonmask);
|
||||
buttonmask = this_button_mask;
|
||||
|
||||
if (_datawheel) {
|
||||
datawheel ();
|
||||
}
|
||||
|
||||
// SHIFT + STOP + PLAY for bling mode?
|
||||
// if (button_changes & ButtonPlay & ButtonStop) {
|
||||
// bling_mode_toggle();
|
||||
// } or something like that
|
||||
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_battery,ButtonBattery);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_backlight,ButtonBacklight);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_trackleft,ButtonTrackLeft);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_trackright,ButtonTrackRight);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_trackrec,ButtonTrackRec);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_trackmute,ButtonTrackMute);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_tracksolo,ButtonTrackSolo);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_undo,ButtonUndo);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_in,ButtonIn);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_out,ButtonOut);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_punch,ButtonPunch);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_loop,ButtonLoop);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_prev,ButtonPrev);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_add,ButtonAdd);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_next,ButtonNext);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_rewind,ButtonRewind);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_fastforward,ButtonFastForward);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_stop,ButtonStop);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_play,ButtonPlay);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_record,ButtonRecord);
|
||||
TRANZPORT_BUTTON_HANDLER(button_event_footswitch,ButtonFootswitch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
333
libs/surfaces/tranzport/general.cc
Normal file
333
libs/surfaces/tranzport/general.cc
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <tranzport_common.h>
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
using namespace PBD;
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <pbd/abstract_ui.cc>
|
||||
// HA, I don't need this anymore
|
||||
#include <slider_gain.h>
|
||||
|
||||
// FIXME, flash recording light when recording and transport is moving
|
||||
int TranzportControlProtocol::lights_show_recording()
|
||||
{
|
||||
return lights_show_normal();
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::show_bling() {
|
||||
lights_show_bling();
|
||||
screen_show_bling();
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::notify(const char *msg) {
|
||||
last_notify=100;
|
||||
if(strlen(msg) < 21) {
|
||||
strcpy(last_notify_msg,msg);
|
||||
} else {
|
||||
strncpy(last_notify_msg,msg,16);
|
||||
last_notify_msg[16] = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::show_notify() {
|
||||
// FIXME: Get width of the notify area somehow
|
||||
if(last_notify==0) {
|
||||
print(1,0," ");
|
||||
last_notify=-1;
|
||||
}
|
||||
if(last_notify > 0) {
|
||||
print(1,0,last_notify_msg);
|
||||
--last_notify;
|
||||
}
|
||||
}
|
||||
|
||||
// Need more bling!
|
||||
|
||||
int TranzportControlProtocol::lights_show_bling()
|
||||
{
|
||||
switch (bling_mode) {
|
||||
case BlingOff: break;
|
||||
case BlingKit: break; // rotate rec/mute/solo/any solo back and forth
|
||||
case BlingRotating: break; // switch between lights
|
||||
case BlingPairs: break; // Show pairs of lights
|
||||
case BlingRows: break; // light each row in sequence
|
||||
case BlingFlashAll: break; // Flash everything randomly
|
||||
case BlingEnter: lights_on(); // Show intro
|
||||
case BlingExit:
|
||||
lights_off();
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TranzportControlProtocol::screen_show_bling()
|
||||
{
|
||||
switch (bling_mode) {
|
||||
case BlingOff: break;
|
||||
case BlingKit: break; // rotate rec/mute/solo/any solo back and forth
|
||||
case BlingRotating: break; // switch between lights
|
||||
case BlingPairs: break; // Show pairs of lights
|
||||
case BlingRows: break; // light each row in sequence
|
||||
case BlingFlashAll: break; // Flash everything randomly
|
||||
case BlingEnter: // Show intro
|
||||
print(0,0,"!!Welcome to Ardour!");
|
||||
print(1,0,"Peace through Music!");
|
||||
break;
|
||||
case BlingExit:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TranzportControlProtocol::lights_show_normal()
|
||||
{
|
||||
/* Track only */
|
||||
|
||||
if (route_table[0]) {
|
||||
boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
|
||||
lights_pending[LightTrackrec] = at && at->record_enabled();
|
||||
lights_pending[LightTrackmute] = route_get_muted(0);
|
||||
lights_pending[LightTracksolo] = route_get_soloed(0);
|
||||
} else {
|
||||
lights_pending[LightTrackrec] = false;
|
||||
lights_pending[LightTracksolo] = false;
|
||||
lights_pending[LightTrackmute] = false;
|
||||
}
|
||||
|
||||
/* Global settings */
|
||||
|
||||
lights_pending[LightLoop] = session->get_play_loop();
|
||||
lights_pending[LightPunch] = Config->get_punch_in() || Config->get_punch_out();
|
||||
lights_pending[LightRecord] = session->get_record_enabled();
|
||||
lights_pending[LightAnysolo] = session->soloing();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TranzportControlProtocol::lights_show_tempo()
|
||||
{
|
||||
// someday soon fiddle with the lights more sanely based on the tempo
|
||||
return lights_show_normal();
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::update_state ()
|
||||
{
|
||||
/* do the text and light updates */
|
||||
|
||||
switch (display_mode) {
|
||||
case DisplayBigMeter:
|
||||
lights_show_tempo();
|
||||
show_meter ();
|
||||
break;
|
||||
|
||||
case DisplayNormal:
|
||||
lights_show_normal();
|
||||
normal_update();
|
||||
break;
|
||||
|
||||
case DisplayConfig:
|
||||
break;
|
||||
|
||||
case DisplayRecording:
|
||||
lights_show_recording();
|
||||
normal_update();
|
||||
break;
|
||||
|
||||
case DisplayRecordingMeter:
|
||||
lights_show_recording();
|
||||
show_meter();
|
||||
break;
|
||||
|
||||
case DisplayBling:
|
||||
show_bling();
|
||||
break;
|
||||
|
||||
case DisplayBlingMeter:
|
||||
lights_show_bling();
|
||||
show_meter();
|
||||
break;
|
||||
}
|
||||
show_notify();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::prev_marker ()
|
||||
{
|
||||
Location *location = session->locations()->first_location_before (session->transport_frame());
|
||||
|
||||
if (location) {
|
||||
session->request_locate (location->start(), session->transport_rolling());
|
||||
notify(location->name().c_str());
|
||||
} else {
|
||||
session->goto_start ();
|
||||
notify("START");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::next_marker ()
|
||||
{
|
||||
Location *location = session->locations()->first_location_after (session->transport_frame());
|
||||
|
||||
if (location) {
|
||||
session->request_locate (location->start(), session->transport_rolling());
|
||||
notify(location->name().c_str());
|
||||
} else {
|
||||
session->request_locate (session->current_end_frame());
|
||||
notify("END ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TranzportControlProtocol::show_current_track ()
|
||||
{
|
||||
char pad[COLUMNS];
|
||||
char *v;
|
||||
int len;
|
||||
if (route_table[0] == 0) {
|
||||
print (0, 0, "---------------");
|
||||
last_track_gain = FLT_MAX;
|
||||
} else {
|
||||
strcpy(pad," ");
|
||||
v = (char *)route_get_name (0).substr (0, 14).c_str();
|
||||
if((len = strlen(v)) > 0) {
|
||||
strncpy(pad,(char *)v,len);
|
||||
}
|
||||
print (0, 0, pad);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void
|
||||
TranzportControlProtocol::step_gain (float increment)
|
||||
{
|
||||
// FIXME: buttonstop is used elsewhere
|
||||
if (buttonmask & ButtonStop) {
|
||||
gain_fraction += 0.001*increment;
|
||||
} else {
|
||||
gain_fraction += 0.01*increment;
|
||||
}
|
||||
|
||||
if (fabsf(gain_fraction) > 2.0) {
|
||||
gain_fraction = 2.0*sign(gain_fraction);
|
||||
}
|
||||
|
||||
route_set_gain (0, slider_position_to_gain (gain_fraction));
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
TranzportControlProtocol::step_gain_up ()
|
||||
{
|
||||
if (buttonmask & ButtonStop) {
|
||||
gain_fraction += 0.001;
|
||||
} else {
|
||||
gain_fraction += 0.01;
|
||||
}
|
||||
|
||||
if (gain_fraction > 2.0) {
|
||||
gain_fraction = 2.0;
|
||||
}
|
||||
|
||||
route_set_gain (0, slider_position_to_gain (gain_fraction));
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::step_gain_down ()
|
||||
{
|
||||
if (buttonmask & ButtonStop) {
|
||||
gain_fraction -= 0.001;
|
||||
} else {
|
||||
gain_fraction -= 0.01;
|
||||
}
|
||||
|
||||
if (gain_fraction < 0.0) {
|
||||
gain_fraction = 0.0;
|
||||
}
|
||||
|
||||
route_set_gain (0, slider_position_to_gain (gain_fraction));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TranzportControlProtocol::next_track ()
|
||||
{
|
||||
ControlProtocol::next_track (current_track_id);
|
||||
gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
|
||||
// notify("NextTrak"); // not needed til we have more modes
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::prev_track ()
|
||||
{
|
||||
ControlProtocol::prev_track (current_track_id);
|
||||
gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
|
||||
// notify("PrevTrak");
|
||||
}
|
||||
|
||||
// This should kind of switch to using notify
|
||||
|
||||
// Was going to keep state around saying to retry or not
|
||||
// haven't got to it yet, still not sure it's a good idea
|
||||
|
||||
void
|
||||
TranzportControlProtocol::print (int row, int col, const char *text) {
|
||||
print_noretry(row,col,text);
|
||||
}
|
||||
|
||||
// -1 on failure
|
||||
// 0 on no damage
|
||||
// count of bit set on damage?
|
||||
|
||||
void
|
||||
TranzportControlProtocol::print_noretry (int row, int col, const char *text)
|
||||
{
|
||||
uint32_t length = strlen (text);
|
||||
if (row*COLUMNS+col+length > (ROWS*COLUMNS)) {
|
||||
return;
|
||||
}
|
||||
// FIXME - be able to print the whole screen at a go.
|
||||
uint32_t t,r,c;
|
||||
std::bitset<ROWS*COLUMNS> mask(screen_invalid);
|
||||
for(r = row, c = col, t = 0 ; t < length; c++,t++) {
|
||||
screen_pending[r][c] = text[t];
|
||||
mask[r*COLUMNS+c] = (screen_current[r][c] != screen_pending[r][c]);
|
||||
}
|
||||
screen_invalid = mask;
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::invalidate()
|
||||
{
|
||||
lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not
|
||||
}
|
||||
314
libs/surfaces/tranzport/init.cc
Normal file
314
libs/surfaces/tranzport/init.cc
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tranzport_common.h>
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
using namespace PBD;
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <pbd/abstract_ui.cc>
|
||||
|
||||
void*
|
||||
TranzportControlProtocol::_monitor_work (void* arg)
|
||||
{
|
||||
return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
|
||||
}
|
||||
|
||||
TranzportControlProtocol::~TranzportControlProtocol ()
|
||||
{
|
||||
set_active (false);
|
||||
}
|
||||
|
||||
int TranzportControlProtocol::rtpriority_set(int priority)
|
||||
{
|
||||
struct sched_param rtparam;
|
||||
int err;
|
||||
char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b';
|
||||
memset (&rtparam, 0, sizeof (rtparam));
|
||||
rtparam.sched_priority = priority; /* XXX should be relative to audio (JACK) thread */
|
||||
// Note - try SCHED_RR with a low limit
|
||||
// - we don't care if we can't write everything this ms
|
||||
// and it will help if we lose the device
|
||||
if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
|
||||
PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Running with realtime privs is bad when you have problems
|
||||
|
||||
int TranzportControlProtocol::rtpriority_unset(int priority)
|
||||
{
|
||||
struct sched_param rtparam;
|
||||
int err;
|
||||
memset (&rtparam, 0, sizeof (rtparam));
|
||||
rtparam.sched_priority = priority;
|
||||
if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
|
||||
PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
|
||||
return 1;
|
||||
}
|
||||
PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
TranzportControlProtocol::set_active (bool yn)
|
||||
{
|
||||
if (yn != _active) {
|
||||
|
||||
if (yn) {
|
||||
|
||||
if (open ()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
|
||||
_active = true;
|
||||
#if TRANZPORT_THREADS
|
||||
if (pthread_create_and_store (X_("tranzport read"), &thread_read, 0, _read_work, this) == 0) {
|
||||
_active_read = true;
|
||||
if (pthread_create_and_store (X_("tranzport write"), &thread_write, 0, _write_work, this) == 0) {
|
||||
_active_write = true;
|
||||
if (pthread_create_and_store (X_("tranzport process"), &thread_process, 0, _process_work, this) == 0) {
|
||||
_active_process = true;
|
||||
if (pthread_create_and_store (X_("tranzport timer"), &thread_timer, 0, _process_timer, this) == 0) {
|
||||
_active_process = true;
|
||||
#endif
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
cerr << "Begin tranzport shutdown\n";
|
||||
// if we got here due to an error, prettifying things will only make it worse
|
||||
// And with threads involved, oh boy...
|
||||
if(!(last_write_error || last_read_error)) {
|
||||
bling_mode = BlingExit;
|
||||
enter_bling_mode();
|
||||
// thread FIXME - wait til all writes are done
|
||||
for(int x = 0; (x < 20/MAX_TRANZPORT_INFLIGHT) && flush(); x++) { usleep(100); }
|
||||
}
|
||||
#if TRANZPORT_THREADS
|
||||
pthread_cancel_one (_thread_timer);
|
||||
pthread_cancel_one (_thread_process);
|
||||
pthread_cancel_one (_thread_read);
|
||||
pthread_cancel_one (_thread_write);
|
||||
#endif
|
||||
pthread_cancel_one (thread);
|
||||
|
||||
cerr << "Tranzport Thread dead\n";
|
||||
close ();
|
||||
_active = false;
|
||||
cerr << "End tranzport shutdown\n";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TranzportControlProtocol::TranzportControlProtocol (Session& s)
|
||||
: ControlProtocol (s, X_("Tranzport"))
|
||||
{
|
||||
/* tranzport controls one track at a time */
|
||||
|
||||
set_route_table_size (1);
|
||||
timeout = 6000; // what is this for?
|
||||
buttonmask = 0;
|
||||
_datawheel = 0;
|
||||
_device_status = STATUS_OFFLINE;
|
||||
udev = 0;
|
||||
current_track_id = 0;
|
||||
last_where = max_frames;
|
||||
wheel_mode = WheelTimeline;
|
||||
wheel_shift_mode = WheelShiftGain;
|
||||
wheel_increment = WheelIncrScreen;
|
||||
bling_mode = BlingEnter;
|
||||
last_notify_msg[0] = '\0';
|
||||
last_notify = 0;
|
||||
timerclear (&last_wheel_motion);
|
||||
last_wheel_dir = 1;
|
||||
last_track_gain = FLT_MAX;
|
||||
last_write_error = 0;
|
||||
last_read_error = 0;
|
||||
display_mode = DisplayBling;
|
||||
gain_fraction = 0.0;
|
||||
invalidate();
|
||||
screen_init();
|
||||
lights_init();
|
||||
// FIXME: Wait til device comes online somewhere
|
||||
// About 3 reads is enough
|
||||
// enter_bling_mode();
|
||||
|
||||
}
|
||||
|
||||
void*
|
||||
TranzportControlProtocol::monitor_work ()
|
||||
{
|
||||
uint8_t buf[8]; // = { 0,0,0,0,0,0,0,0 };
|
||||
int val = 0, pending = 0;
|
||||
bool first_time = true;
|
||||
uint8_t offline = 0;
|
||||
|
||||
PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
|
||||
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
|
||||
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
||||
rtpriority_set();
|
||||
inflight=0;
|
||||
int intro = 20;
|
||||
|
||||
// wait for the device to come online
|
||||
invalidate();
|
||||
screen_init();
|
||||
lights_init();
|
||||
update_state();
|
||||
// There has to be some specific command to enable the device!!
|
||||
// while((val = read(buf,DEFAULT_USB_TIMEOUT*5)) == -110 && pending !=0) {
|
||||
// pending = lights_flush(); // poke the device for a while
|
||||
// }
|
||||
|
||||
// pending = 1;
|
||||
// while(intro-- > 0 && pending != 0) {
|
||||
// usleep(1000);
|
||||
// pending = screen_flush(); // kinder, gentler init
|
||||
// }
|
||||
// usleep(1000);
|
||||
// lights_on();
|
||||
// while(flush()!=0) ;
|
||||
// lights_off();
|
||||
display_mode = DisplayNormal;
|
||||
|
||||
while (true) {
|
||||
|
||||
/* bInterval for this beastie is 10ms */
|
||||
|
||||
if (_device_status == STATUS_OFFLINE) {
|
||||
first_time = true; offline++;
|
||||
#if TRANZPORT_DEBUG > 3
|
||||
if(offline == 1) {
|
||||
cerr << "Transport has gone offline\n";
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
offline = 0; // hate writing this
|
||||
}
|
||||
unsigned int s = (last_write_error == 0) | ((last_read_error == 0) << 1);
|
||||
switch (s) {
|
||||
case 0: val = read(buf,DEFAULT_USB_TIMEOUT); break;
|
||||
case 1: val = read(buf,DEFAULT_USB_TIMEOUT); break;
|
||||
case 2: val = read(buf,DEFAULT_USB_TIMEOUT); break;
|
||||
case 3: val = read(buf,DEFAULT_USB_TIMEOUT*2); break; // Hoo, boy, we're in trouble
|
||||
default: break; // not reached
|
||||
}
|
||||
|
||||
#if DEBUG_TRANZPORT_BITS > 9
|
||||
if(_device_status != STATUS_OFFLINE && _device_status != STATUS_ONLINE && _device_status != STATUS_OK) {
|
||||
printf("The device has more status bits than off or online: %d\n",_device_status);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DEBUG_TRANZPORT_BITS > 99
|
||||
if (val != 8) {
|
||||
printf("val = %d errno = %d\n",val,errno);
|
||||
buf[0] = buf[1] = buf[2] = buf[3] =
|
||||
buf[4] = buf[5] = buf[6] = buf[7] =
|
||||
buf[8] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(val == 8) {
|
||||
last_write_error = 0;
|
||||
process (buf);
|
||||
}
|
||||
|
||||
#if DEBUG_TRANZPORT > 9
|
||||
if(inflight > 1) printf("Inflight: %d\n", inflight);
|
||||
#endif
|
||||
|
||||
if (_device_status == STATUS_ONLINE) {
|
||||
if (first_time) {
|
||||
invalidate();
|
||||
lcd_clear ();
|
||||
lights_off ();
|
||||
first_time = false;
|
||||
last_write_error = 0;
|
||||
offline = 0;
|
||||
pending = 3; // Give some time for the device to recover
|
||||
}
|
||||
#if DEBUG_TRANZPORT_BITS > 10
|
||||
// Perhaps an online message indicates something
|
||||
|
||||
if(_device_status != buf[1]) {
|
||||
printf("WTF- val: %d, device status != buf! %d != %d \n",val,_device_status,buf[1]); _device_status = buf[1];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG_TRANZPORT_BITS > 10
|
||||
|
||||
if(val == 8) {
|
||||
|
||||
if(_device_status == STATUS_ONLINE) {
|
||||
printf("ONLINE : %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
|
||||
}
|
||||
if(_device_status == STATUS_OFFLINE) {
|
||||
printf("OFFLINE : %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
|
||||
}
|
||||
|
||||
if(_device_status == STATUS_OK) {
|
||||
printf("OK : %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* update whatever needs updating */
|
||||
if(last_write_error == 0 && (_device_status == STATUS_ONLINE || _device_status == STATUS_OK)) {
|
||||
update_state ();
|
||||
|
||||
/* still struggling with a good means of exerting flow control without having to create threads */
|
||||
// pending = flush();
|
||||
|
||||
if(pending == 0) {
|
||||
pending = flush();
|
||||
} else {
|
||||
if(inflight > 0) {
|
||||
pending = --inflight; // we just did a whole bunch of writes so wait
|
||||
} else {
|
||||
pending = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// pending = 0;
|
||||
}
|
||||
return (void*) 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <control_protocol/control_protocol.h>
|
||||
#include "tranzport_control_protocol.h"
|
||||
|
||||
|
|
|
|||
95
libs/surfaces/tranzport/io.cc
Normal file
95
libs/surfaces/tranzport/io.cc
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* The routines in here should know absolutely nothing about how io is actually done */
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
int
|
||||
TranzportControlProtocol::flush ()
|
||||
{
|
||||
int pending = 0;
|
||||
|
||||
// Always write the lights first
|
||||
if(!(pending = lights_flush())) {
|
||||
pending = screen_flush();
|
||||
}
|
||||
|
||||
#if DEBUG_TRANZPORT_BITS > 9
|
||||
int s;
|
||||
if(s = (screen_invalid.count())) { // + lights_invalid.count())) {
|
||||
printf("VALID : %s %s\n",
|
||||
screen_invalid.to_string().c_str(),
|
||||
lights_invalid.to_string().c_str());
|
||||
printf("CURR : %s %s\n",
|
||||
screen_invalid.to_string().c_str(),
|
||||
lights_current.to_string().c_str());
|
||||
printf("PENDING : %s %s\n",
|
||||
screen_invalid.to_string().c_str(),
|
||||
lights_pending.to_string().c_str());
|
||||
#if DEBUG_TRANZPORT_BITS > 10
|
||||
printf("invalid bits: %d\n",s);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return pending;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
TranzportControlProtocol::lights_flush ()
|
||||
{
|
||||
std::bitset<LIGHTS> light_state;
|
||||
light_state = lights_pending ^ lights_current;
|
||||
if ( (light_state.none() || lights_invalid.none()))
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
#if DEBUG_TRANZPORT_LIGHTS
|
||||
printf("LPEND : %s\n", lights_pending.to_string().c_str());
|
||||
printf("LCURR : %s\n", lights_current.to_string().c_str());
|
||||
#endif
|
||||
|
||||
// if ever we thread reads/writes STATUS_OK will have to move into the loop
|
||||
int i;
|
||||
|
||||
if ( _device_status == STATUS_OK || _device_status == STATUS_ONLINE) {
|
||||
for (i = 0; i<LIGHTS; i++) {
|
||||
if(light_state[i]) {
|
||||
if(light_set ((LightID)i,lights_pending[i])) {
|
||||
#if DEBUG_TRANZPORT_LIGHTS > 2
|
||||
printf("Did %d light writes\n",i);
|
||||
#endif
|
||||
return light_state.count();
|
||||
} else {
|
||||
light_state[i] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
light_state = lights_pending ^ lights_current;
|
||||
#if DEBUG_TRANZPORT_LIGHTS > 2
|
||||
printf("Did %d light writes, left: %d\n",i, light_state.count());
|
||||
#endif
|
||||
|
||||
return light_state.count();
|
||||
}
|
||||
144
libs/surfaces/tranzport/io_kernel.cc
Normal file
144
libs/surfaces/tranzport/io_kernel.cc
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#if HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include "tranzport_control_protocol.h"
|
||||
|
||||
// Something like open(/dev/surface/tranzport/event) for reading and raw for writing) would be better in the long run
|
||||
// Also support for multiple tranzports needs to be figured out
|
||||
// And bulk reads/writes in general
|
||||
|
||||
bool
|
||||
TranzportControlProtocol::probe ()
|
||||
{
|
||||
if((udev = ::open(TRANZPORT_DEVICE,O_RDWR))> 0) {
|
||||
::close(udev);
|
||||
return true;
|
||||
}
|
||||
error << _("Tranzport: Can't open device for Read/Write: ") << endmsg;
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::open ()
|
||||
{
|
||||
if((udev=::open(TRANZPORT_DEVICE,O_RDWR))> 0) {
|
||||
return(udev);
|
||||
}
|
||||
error << _("Tranzport: no device detected") << endmsg;
|
||||
return udev;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::close ()
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (udev < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ret = ::close (udev)) != 0) {
|
||||
error << _("Tranzport: cannot close device") << endmsg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// someday do buffered reads, presently this does blocking reads, which is bad...
|
||||
|
||||
int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
|
||||
{
|
||||
last_read_error = ::read (udev, (char *) buf, 8);
|
||||
switch(errno) {
|
||||
case -ENOENT:
|
||||
case -ENXIO:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
case -ENODEV:
|
||||
cerr << "Tranzport disconnected, errno: " << last_read_error;
|
||||
set_active(false);
|
||||
break;
|
||||
case -ETIMEDOUT: // This is not normal, but lets see what happened
|
||||
cerr << "Tranzport read timed out, errno: " << last_read_error;
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_TRANZPORT
|
||||
cerr << "Got an unknown error on read:" << last_read_error "\n";
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return last_read_error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
|
||||
{
|
||||
// inflight is now taken care of by the driver, but...
|
||||
if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
|
||||
int val = ::write (udev, (char*) cmd, 8);
|
||||
|
||||
if (val < 0 && val !=8) {
|
||||
#if DEBUG_TRANZPORT
|
||||
printf("write failed: %d\n", val);
|
||||
#endif
|
||||
last_write_error = errno;
|
||||
switch(last_write_error) {
|
||||
case -ENOENT:
|
||||
case -ENXIO:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
case -ENODEV:
|
||||
cerr << "Tranzport disconnected, errno: " << last_write_error;
|
||||
set_active(false);
|
||||
break;
|
||||
case -ETIMEDOUT: // This is not normal but
|
||||
cerr << "Tranzport disconnected, errno: " << last_write_error;
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_TRANZPORT
|
||||
cerr << "Got an unknown error on read:" << last_write_error "\n";
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return last_write_error;
|
||||
}
|
||||
|
||||
last_write_error = 0;
|
||||
++inflight;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
|
||||
{
|
||||
return (write_noretry(cmd,timeout_override));
|
||||
}
|
||||
|
||||
// FIXME - install poll semantics
|
||||
#endif /* HAVE_TRANZPORT_KERNEL_DRIVER */
|
||||
|
||||
26
libs/surfaces/tranzport/io_midi.cc
Normal file
26
libs/surfaces/tranzport/io_midi.cc
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* io_midi: Implements reading and writing tranzport events via the normal
|
||||
tranzport midi specification */
|
||||
|
||||
/* One day
|
||||
#include <tranzport_control_protocol.h>
|
||||
*/
|
||||
232
libs/surfaces/tranzport/io_usb.cc
Normal file
232
libs/surfaces/tranzport/io_usb.cc
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
#if !HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
using namespace PBD;
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <pbd/abstract_ui.cc>
|
||||
|
||||
// I note that these usb specific open, close, probe, read routines are basically
|
||||
// pure boilerplate and could easily be abstracted elsewhere
|
||||
|
||||
bool
|
||||
TranzportControlProtocol::probe ()
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct usb_device *dev;
|
||||
|
||||
usb_init();
|
||||
usb_find_busses();
|
||||
usb_find_devices();
|
||||
|
||||
for (bus = usb_busses; bus; bus = bus->next) {
|
||||
|
||||
for(dev = bus->devices; dev; dev = dev->next) {
|
||||
if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::open ()
|
||||
{
|
||||
struct usb_bus *bus;
|
||||
struct usb_device *dev;
|
||||
|
||||
usb_init();
|
||||
usb_find_busses();
|
||||
usb_find_devices();
|
||||
|
||||
for (bus = usb_busses; bus; bus = bus->next) {
|
||||
|
||||
for(dev = bus->devices; dev; dev = dev->next) {
|
||||
if (dev->descriptor.idVendor != VENDORID)
|
||||
continue;
|
||||
if (dev->descriptor.idProduct != PRODUCTID)
|
||||
continue;
|
||||
return open_core (dev);
|
||||
}
|
||||
}
|
||||
|
||||
cerr << _("Tranzport: no device detected") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::open_core (struct usb_device* dev)
|
||||
{
|
||||
if (!(udev = usb_open (dev))) {
|
||||
cerr << _("Tranzport: cannot open USB transport") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (usb_claim_interface (udev, 0) < 0) {
|
||||
cerr << _("Tranzport: cannot claim USB interface") << endmsg;
|
||||
usb_close (udev);
|
||||
udev = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (usb_set_configuration (udev, 1) < 0) {
|
||||
cerr << _("Tranzport: cannot configure USB interface") << endmsg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::close ()
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (udev == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usb_release_interface (udev, 0) < 0) {
|
||||
cerr << _("Tranzport: cannot release interface") << endmsg;
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (usb_close (udev)) {
|
||||
cerr << _("Tranzport: cannot close device") << endmsg;
|
||||
udev = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
|
||||
{
|
||||
last_read_error = usb_interrupt_read (udev, READ_ENDPOINT, (char *) buf, 8, timeout_override);
|
||||
switch(last_read_error) {
|
||||
case -ENOENT:
|
||||
case -ENXIO:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
case -ENODEV:
|
||||
cerr << "Tranzport disconnected, errno: " << last_read_error;
|
||||
set_active(false);
|
||||
case -ETIMEDOUT: // This is normal
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_TRANZPORT
|
||||
cerr << "Got an unknown error on read:" << last_read_error "\n";
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return last_read_error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
|
||||
{
|
||||
int val;
|
||||
if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
|
||||
val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
|
||||
|
||||
if (val < 0 && val !=8) {
|
||||
#if DEBUG_TRANZPORT
|
||||
printf("usb_interrupt_write failed: %d\n", val);
|
||||
#endif
|
||||
last_write_error = val;
|
||||
switch(last_write_error) {
|
||||
case -ENOENT:
|
||||
case -ENXIO:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
case -ENODEV:
|
||||
cerr << "Tranzport disconnected, errno: " << last_write_error;
|
||||
set_active(false);
|
||||
case -ETIMEDOUT: // This is normal
|
||||
break;
|
||||
default:
|
||||
#if DEBUG_TRANZPORT
|
||||
cerr << "Got an unknown error on read:" << last_write_error "\n";
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
last_write_error = 0;
|
||||
++inflight;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
|
||||
{
|
||||
#if MAX_RETRY > 1
|
||||
int val;
|
||||
int retry = 0;
|
||||
if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
|
||||
|
||||
while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
|
||||
printf("usb_interrupt_write failed, retrying: %d\n", val);
|
||||
}
|
||||
|
||||
if (retry == MAX_RETRY) {
|
||||
printf("Too many retries on a tranzport write, aborting\n");
|
||||
}
|
||||
|
||||
if (val < 0) {
|
||||
printf("usb_interrupt_write failed: %d\n", val);
|
||||
return val;
|
||||
}
|
||||
if (val != 8) {
|
||||
printf("usb_interrupt_write failed: %d\n", val);
|
||||
return -1;
|
||||
}
|
||||
++inflight;
|
||||
return 0;
|
||||
#else
|
||||
return (write_noretry(cmd,timeout_override));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
120
libs/surfaces/tranzport/lcd.cc
Normal file
120
libs/surfaces/tranzport/lcd.cc
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
// doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon
|
||||
|
||||
bool TranzportControlProtocol::lcd_damage()
|
||||
{
|
||||
screen_invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TranzportControlProtocol::lcd_damage (int row, int col, int length)
|
||||
{
|
||||
std::bitset<ROWS*COLUMNS> mask1(0);
|
||||
// there's an intrinsic to do this fast, darn it, or I'm just sleepy
|
||||
for (int i = 0; i < length; i++) { mask1[i] = 1; }
|
||||
std::bitset<ROWS*COLUMNS> mask(mask1 << (row*COLUMNS+col));
|
||||
screen_invalid |= mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Still working on the layering, arguably screen_invalid should be lcd_invalid
|
||||
// or vice versa
|
||||
|
||||
bool TranzportControlProtocol::lcd_isdamaged ()
|
||||
{
|
||||
if(screen_invalid.any()) {
|
||||
#if DEBUG_TRANZPORT > 5
|
||||
printf("LCD is damaged somewhere, should redraw it\n");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TranzportControlProtocol::lcd_isdamaged (int row, int col, int length)
|
||||
{
|
||||
// there's an intrinsic to do this fast, darn it
|
||||
std::bitset<ROWS*COLUMNS> mask1(0);
|
||||
for (int i = 0; i < length; i++) { mask1[i] = 1; }
|
||||
std::bitset<ROWS*COLUMNS> mask(mask1 << (row*COLUMNS+col));
|
||||
mask &= screen_invalid;
|
||||
if(mask.any()) {
|
||||
#if DEBUG_TRANZPORT > 5
|
||||
printf("row: %d,col: %d is damaged, should redraw it\n", row,col);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// lcd_clear would be a separate function for a smart display
|
||||
// here it does nothing, but for the sake of completeness it should
|
||||
// probably write the lcd, and while I'm on the topic it should probably
|
||||
// take a row, col, length argument....
|
||||
|
||||
void
|
||||
TranzportControlProtocol::lcd_clear ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// These lcd commands are not universally used yet and may drop out of the api
|
||||
|
||||
int
|
||||
TranzportControlProtocol::lcd_flush ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override)
|
||||
{
|
||||
int result;
|
||||
#if (DEBUG_TRANZPORT_SCREEN > 0)
|
||||
printf("VALID : %s\n", (screen_invalid.to_string()).c_str());
|
||||
#endif
|
||||
if ((result = write(cmd,timeout_override))) {
|
||||
#if DEBUG_TRANZPORT > 4
|
||||
printf("usb screen update failed for some reason... why? \nresult, cmd and data were %d %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
result, cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]);
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::lcd_fill (uint8_t fill_char)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::lcd_print (int row, int col, const char* text)
|
||||
{
|
||||
print(row,col,text);
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::lcd_print_noretry (int row, int col, const char* text)
|
||||
{
|
||||
print(row,col,text);
|
||||
}
|
||||
95
libs/surfaces/tranzport/lights.cc
Normal file
95
libs/surfaces/tranzport/lights.cc
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
// Lights are buffered, and arguably these functions should be eliminated or inlined
|
||||
|
||||
void
|
||||
TranzportControlProtocol::lights_on ()
|
||||
{
|
||||
lights_pending.set();
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::lights_off ()
|
||||
{
|
||||
lights_pending.reset();
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::light_on (LightID light)
|
||||
{
|
||||
lights_pending.set(light);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::light_off (LightID light)
|
||||
{
|
||||
lights_pending.reset(light);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::lights_init()
|
||||
{
|
||||
lights_invalid.set();
|
||||
lights_flash = lights_pending = lights_current.reset();
|
||||
}
|
||||
|
||||
|
||||
// Now that all this is bitsets, I don't see much
|
||||
// need for these 4 to remain in the API
|
||||
|
||||
void TranzportControlProtocol::light_validate (LightID light)
|
||||
{
|
||||
lights_invalid.reset(light);
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::light_invalidate (LightID light)
|
||||
{
|
||||
lights_invalid.set(light);
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::lights_validate ()
|
||||
{
|
||||
lights_invalid.reset();
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::lights_invalidate ()
|
||||
{
|
||||
lights_invalid.set();
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::light_set (LightID light, bool offon)
|
||||
{
|
||||
uint8_t cmd[8];
|
||||
cmd[0] = 0x00; cmd[1] = 0x00; cmd[2] = light; cmd[3] = offon;
|
||||
cmd[4] = 0x00; cmd[5] = 0x00; cmd[6] = 0x00; cmd[7] = 0x00;
|
||||
|
||||
if (write (cmd) == 0) {
|
||||
lights_current[light] = offon;
|
||||
lights_invalid.reset(light);
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
21
libs/surfaces/tranzport/meter.cc
Normal file
21
libs/surfaces/tranzport/meter.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* Generic support for character based metering on a track */
|
||||
107
libs/surfaces/tranzport/mode.cc
Normal file
107
libs/surfaces/tranzport/mode.cc
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
void
|
||||
TranzportControlProtocol::normal_update ()
|
||||
{
|
||||
show_current_track ();
|
||||
show_transport_time ();
|
||||
show_track_gain ();
|
||||
show_wheel_mode ();
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::next_display_mode ()
|
||||
{
|
||||
switch (display_mode) {
|
||||
|
||||
case DisplayNormal:
|
||||
enter_big_meter_mode();
|
||||
break;
|
||||
|
||||
case DisplayBigMeter:
|
||||
enter_normal_display_mode();
|
||||
break;
|
||||
|
||||
case DisplayRecording:
|
||||
enter_normal_display_mode();
|
||||
break;
|
||||
|
||||
case DisplayRecordingMeter:
|
||||
enter_big_meter_mode();
|
||||
break;
|
||||
|
||||
case DisplayConfig:
|
||||
case DisplayBling:
|
||||
case DisplayBlingMeter:
|
||||
enter_normal_display_mode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: There should be both enter and exits
|
||||
// EXIT would erase the portions of the screen being written
|
||||
// to.
|
||||
/* not sure going macro crazy is a good idea
|
||||
#define DECLARE_ENTER_MODE(mode,modename) void TranzportControlProtocol::enter_##mode##_mode() \{\screen_clear(); lights_off(); display_mode=Display##modename;\;
|
||||
*/
|
||||
void
|
||||
TranzportControlProtocol::enter_recording_mode ()
|
||||
{
|
||||
screen_clear ();
|
||||
lights_off ();
|
||||
display_mode = DisplayRecording;
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::enter_bling_mode ()
|
||||
{
|
||||
screen_clear ();
|
||||
lights_off ();
|
||||
display_mode = DisplayBling;
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::enter_config_mode ()
|
||||
{
|
||||
lights_off ();
|
||||
screen_clear ();
|
||||
display_mode = DisplayConfig;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TranzportControlProtocol::enter_big_meter_mode ()
|
||||
{
|
||||
lights_off (); // it will clear the screen for you
|
||||
last_meter_fill = 0;
|
||||
display_mode = DisplayBigMeter;
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::enter_normal_display_mode ()
|
||||
{
|
||||
lights_off ();
|
||||
screen_clear ();
|
||||
display_mode = DisplayNormal;
|
||||
}
|
||||
|
||||
21
libs/surfaces/tranzport/mode_loop.cc
Normal file
21
libs/surfaces/tranzport/mode_loop.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* placeholder for Marker Mode: Edit Markers, Setup Loops, and Punch in points */
|
||||
21
libs/surfaces/tranzport/mode_tuner.cc
Normal file
21
libs/surfaces/tranzport/mode_tuner.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* Placeholder for a tuner mode at some point */
|
||||
34
libs/surfaces/tranzport/panner.cc
Normal file
34
libs/surfaces/tranzport/panner.cc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <tranzport_common.h>
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
void
|
||||
TranzportControlProtocol::step_pan_right ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::step_pan_left ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
93
libs/surfaces/tranzport/screen.cc
Normal file
93
libs/surfaces/tranzport/screen.cc
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
void
|
||||
TranzportControlProtocol::screen_clear ()
|
||||
{
|
||||
const char *blank = " ";
|
||||
print(0,0,blank);
|
||||
print(1,0,blank);
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::screen_invalidate ()
|
||||
{
|
||||
screen_invalid.set();
|
||||
for(int row = 0; row < ROWS; row++) {
|
||||
for(int col = 0; col < COLUMNS; col++) {
|
||||
screen_current[row][col] = 0x7f;
|
||||
screen_pending[row][col] = ' ';
|
||||
screen_flash[row][col] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::screen_validate ()
|
||||
{
|
||||
}
|
||||
|
||||
void TranzportControlProtocol::screen_init ()
|
||||
{
|
||||
screen_invalidate();
|
||||
}
|
||||
|
||||
// FIXME: Switch to a column oriented flush to make the redraw of the
|
||||
// meters look better
|
||||
|
||||
int
|
||||
TranzportControlProtocol::screen_flush ()
|
||||
{
|
||||
int cell = 0, row=0, col_base, pending = 0;
|
||||
const unsigned long CELL_BITS = 0x0F;
|
||||
if ( _device_status == STATUS_OFFLINE) { return (-1); }
|
||||
|
||||
std::bitset<ROWS*COLUMNS> mask(CELL_BITS);
|
||||
std::bitset<ROWS*COLUMNS> imask(CELL_BITS);
|
||||
for(cell = 0; cell < 10 && pending == 0; cell++) {
|
||||
mask = imask << (cell*4);
|
||||
if((screen_invalid & mask).any()) {
|
||||
/* something in this cell is different, so dump the cell to the device. */
|
||||
#if DEBUG_TRANZPORT_SCREEN
|
||||
printf("MASK : %s\n", mask.to_string().c_str());
|
||||
#endif
|
||||
if(cell > 4) { row = 1; } else { row = 0; }
|
||||
col_base = (cell*4)%COLUMNS;
|
||||
|
||||
uint8_t cmd[8];
|
||||
cmd[0] = 0x00;
|
||||
cmd[1] = 0x01;
|
||||
cmd[2] = cell;
|
||||
cmd[3] = screen_pending[row][col_base];
|
||||
cmd[4] = screen_pending[row][col_base+1];
|
||||
cmd[5] = screen_pending[row][col_base+2];
|
||||
cmd[6] = screen_pending[row][col_base+3];
|
||||
cmd[7] = 0x00;
|
||||
|
||||
if((pending = lcd_write(cmd)) == 0) {
|
||||
/* successful write: copy to current cached display */
|
||||
screen_invalid &= mask.flip();
|
||||
memcpy (&screen_current[row][col_base], &screen_pending[row][col_base], 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
|
||||
400
libs/surfaces/tranzport/show.cc
Normal file
400
libs/surfaces/tranzport/show.cc
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pbd/pthread_utils.h>
|
||||
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/audio_track.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/tempo.h>
|
||||
#include <ardour/location.h>
|
||||
#include <ardour/dB.h>
|
||||
|
||||
#include <tranzport_control_protocol.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
using namespace PBD;
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <pbd/abstract_ui.cc>
|
||||
|
||||
float
|
||||
log_meter (float db)
|
||||
{
|
||||
float def = 0.0f; /* Meter deflection %age */
|
||||
|
||||
if (db < -70.0f) return 0.0f;
|
||||
if (db > 6.0f) return 1.0f;
|
||||
|
||||
if (db < -60.0f) {
|
||||
def = (db + 70.0f) * 0.25f;
|
||||
} else if (db < -50.0f) {
|
||||
def = (db + 60.0f) * 0.5f + 2.5f;
|
||||
} else if (db < -40.0f) {
|
||||
def = (db + 50.0f) * 0.75f + 7.5f;
|
||||
} else if (db < -30.0f) {
|
||||
def = (db + 40.0f) * 1.5f + 15.0f;
|
||||
} else if (db < -20.0f) {
|
||||
def = (db + 30.0f) * 2.0f + 30.0f;
|
||||
} else if (db < 6.0f) {
|
||||
def = (db + 20.0f) * 2.5f + 50.0f;
|
||||
}
|
||||
|
||||
/* 115 is the deflection %age that would be
|
||||
when db=6.0. this is an arbitrary
|
||||
endpoint for our scaling.
|
||||
*/
|
||||
|
||||
return def/115.0f;
|
||||
}
|
||||
|
||||
#define TRANZ_U 0x1 /* upper */
|
||||
#define TRANZ_BL 0x2 /* lower left */
|
||||
#define TRANZ_Q2 0x3 /* 2 quadrant block */
|
||||
#define TRANZ_ULB 0x4 /* Upper + lower left */
|
||||
#define TRANZ_L 0x5 /* lower */
|
||||
#define TRANZ_UBL 0x6 /* upper left + bottom all */
|
||||
#define TRANZ_Q4 0x7 /* 4 quadrant block */
|
||||
#define TRANZ_UL 0x08 /* upper left */
|
||||
|
||||
// Shift Space - switches your "view"
|
||||
// Currently defined views are:
|
||||
// BigMeter
|
||||
//
|
||||
// Shift Record - SAVE SNAPSHOT
|
||||
// Somewhere I was rewriting this
|
||||
// Other meters
|
||||
// Inverted - show meters "inside out" For example 4 meters covering 2 cells each, and the
|
||||
//
|
||||
// each 4 character cell could be an 8 bar meter = 10 meters!
|
||||
// Dual Meter mode - master and current track
|
||||
// We have 16 rows of pixels so we COULD do a vertical meter
|
||||
// BEAT BLOCKS - For each beat, flash a 8 block (could use the center for vertical meters)
|
||||
// Could have something generic that could handle up to /20 time
|
||||
// Odd times could flash the whole top bar for the first beat
|
||||
|
||||
|
||||
// Vertical Meter _ .colon - + ucolon A P R I H FULLBLACK
|
||||
// MV@$%&*()-
|
||||
|
||||
// 3 char block rotating beat `\'/
|
||||
// 1 char rotating beat {/\}
|
||||
// 4 char in block rotating beat {/\}
|
||||
// {\/)
|
||||
|
||||
void TranzportControlProtocol::show_mini_meter()
|
||||
{
|
||||
// FIXME - show the current marker in passing
|
||||
const int meter_buf_size = 41;
|
||||
static uint32_t last_meter_fill_l = 0;
|
||||
static uint32_t last_meter_fill_r = 0;
|
||||
uint32_t meter_size;
|
||||
|
||||
float speed = fabsf(session->transport_speed());
|
||||
char buf[meter_buf_size];
|
||||
|
||||
if (speed == 1.0) {
|
||||
meter_size = 32;
|
||||
}
|
||||
|
||||
if (speed == 0.0) {
|
||||
meter_size = 20; // not actually reached
|
||||
}
|
||||
|
||||
if (speed > 0.0 && (speed < 1.0)) {
|
||||
meter_size = 20; // may shrink more one day
|
||||
}
|
||||
|
||||
if (speed > 1.0 && (speed < 2.0)) {
|
||||
meter_size = 20;
|
||||
}
|
||||
|
||||
if (speed >= 2.0) {
|
||||
meter_size = 24;
|
||||
}
|
||||
|
||||
|
||||
// you only seem to get a route_table[0] == 0 on moving forward - bug in next_track?
|
||||
|
||||
if (route_table[0] == 0) {
|
||||
// Principle of least surprise
|
||||
print (1, 0, "NoAUDIO ");
|
||||
return;
|
||||
}
|
||||
|
||||
float level_l = route_get_peak_input_power (0, 0);
|
||||
float fraction_l = log_meter (level_l);
|
||||
|
||||
// how to figure out if we are mono?
|
||||
|
||||
float level_r = route_get_peak_input_power (0, 1);
|
||||
float fraction_r = log_meter (level_r);
|
||||
|
||||
uint32_t fill_left = (uint32_t) floor (fraction_l * ((int) meter_size));
|
||||
uint32_t fill_right = (uint32_t) floor (fraction_r * ((int) meter_size));
|
||||
|
||||
if (fill_left == last_meter_fill_l && fill_right == last_meter_fill_r && !lcd_isdamaged(1,0,meter_size/2)) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
last_meter_fill_l = fill_left; last_meter_fill_r = fill_right;
|
||||
|
||||
// give some feedback when overdriving - override yellow and red lights
|
||||
|
||||
if (fraction_l > 0.96 || fraction_r > 0.96) {
|
||||
light_on (LightLoop);
|
||||
}
|
||||
|
||||
if (fraction_l == 1.0 || fraction_r == 1.0) {
|
||||
light_on (LightTrackrec);
|
||||
}
|
||||
|
||||
const uint8_t char_map[16] = { ' ', TRANZ_UL,
|
||||
TRANZ_U, TRANZ_U,
|
||||
TRANZ_BL, TRANZ_Q2,
|
||||
TRANZ_Q2, TRANZ_ULB,
|
||||
TRANZ_L, TRANZ_UBL,
|
||||
' ',' ',
|
||||
TRANZ_L, TRANZ_UBL,
|
||||
TRANZ_Q4,TRANZ_Q4
|
||||
};
|
||||
unsigned int val,j,i;
|
||||
|
||||
for(j = 1, i = 0; i < meter_size/2; i++, j+=2) {
|
||||
val = (fill_left >= j) | ((fill_left >= j+1) << 1) |
|
||||
((fill_right >=j) << 2) | ((fill_right >= j+1) << 3);
|
||||
buf[i] = char_map[val];
|
||||
}
|
||||
|
||||
/* print() requires this */
|
||||
|
||||
buf[meter_size/2] = '\0';
|
||||
|
||||
print (1, 0, buf);
|
||||
|
||||
/* Add a peak bar, someday do falloff */
|
||||
|
||||
// char peak[2]; peak[0] = ' '; peak[1] = '\0';
|
||||
// if(fraction_l == 1.0 || fraction_r == 1.0) peak[0] = 'P';
|
||||
// print (1,8,peak); // Put a peak meter - P in if we peaked.
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::show_meter ()
|
||||
{
|
||||
// you only seem to get a route_table[0] on moving forward - bug elsewhere
|
||||
if (route_table[0] == 0) {
|
||||
// Principle of least surprise
|
||||
print (0, 0, "No audio to meter!!!");
|
||||
print (1, 0, "Select another track");
|
||||
return;
|
||||
}
|
||||
|
||||
float level = route_get_peak_input_power (0, 0);
|
||||
float fraction = log_meter (level);
|
||||
|
||||
/* Someday add a peak bar*/
|
||||
|
||||
/* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
|
||||
the screen is 20 chars wide, so we can display 40 different levels. compute the level,
|
||||
then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
|
||||
*/
|
||||
|
||||
uint32_t fill = (uint32_t) floor (fraction * 40);
|
||||
char buf[21];
|
||||
uint32_t i;
|
||||
|
||||
if (fill == last_meter_fill) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
last_meter_fill = fill;
|
||||
|
||||
bool add_single_level = (fill % 2 != 0);
|
||||
fill /= 2;
|
||||
|
||||
if (fraction > 0.96) {
|
||||
light_on (LightLoop);
|
||||
}
|
||||
|
||||
|
||||
if (fraction == 1.0) {
|
||||
light_on (LightTrackrec);
|
||||
}
|
||||
|
||||
|
||||
/* add all full steps */
|
||||
|
||||
for (i = 0; i < fill; ++i) {
|
||||
buf[i] = 0x07; /* tranzport special code for 4 quadrant LCD block */
|
||||
}
|
||||
|
||||
/* add a possible half-step */
|
||||
|
||||
if (i < 20 && add_single_level) {
|
||||
buf[i] = 0x03; /* tranzport special code for 2 left quadrant LCD block */
|
||||
++i;
|
||||
}
|
||||
|
||||
/* fill rest with space */
|
||||
|
||||
for (; i < 20; ++i) {
|
||||
buf[i] = ' ';
|
||||
}
|
||||
|
||||
/* print() requires this */
|
||||
|
||||
buf[21] = '\0';
|
||||
|
||||
print (0, 0, buf);
|
||||
print (1, 0, buf);
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::show_bbt (nframes_t where)
|
||||
{
|
||||
if (where != last_where) {
|
||||
char buf[16];
|
||||
BBT_Time bbt;
|
||||
|
||||
// When recording or playing back < 1.0 speed do 1 or 2
|
||||
// FIXME - clean up state machine & break up logic
|
||||
// this has to co-operate with the mini-meter and
|
||||
// this is NOT the right way.
|
||||
|
||||
session->tempo_map().bbt_time (where, bbt);
|
||||
last_bars = bbt.bars;
|
||||
last_beats = bbt.beats;
|
||||
last_ticks = bbt.ticks;
|
||||
last_where = where;
|
||||
|
||||
float speed = fabsf(session->transport_speed());
|
||||
|
||||
if (speed == 1.0) {
|
||||
sprintf (buf, "%03" PRIu32 "%1" PRIu32, bbt.bars,bbt.beats); // switch to hex one day
|
||||
print (1, 16, buf);
|
||||
}
|
||||
|
||||
if (speed == 0.0) {
|
||||
sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
|
||||
print (1, 10, buf);
|
||||
}
|
||||
|
||||
if (speed > 0.0 && (speed < 1.0)) {
|
||||
sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
|
||||
print (1, 10, buf);
|
||||
}
|
||||
|
||||
if (speed > 1.0 && (speed < 2.0)) {
|
||||
sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
|
||||
print (1, 10, buf);
|
||||
}
|
||||
|
||||
if (speed >= 2.0) {
|
||||
sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%02" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
|
||||
print (1, 12, buf);
|
||||
}
|
||||
|
||||
TempoMap::Metric m (session->tempo_map().metric_at (where));
|
||||
|
||||
// the lights stop working well at above 100 bpm so don't bother
|
||||
if(m.tempo().beats_per_minute() < 101.0 && (speed > 0.0)) {
|
||||
|
||||
// something else can reset these, so we need to
|
||||
|
||||
lights_pending[LightRecord] = false;
|
||||
lights_pending[LightAnysolo] = false;
|
||||
switch(last_beats) {
|
||||
case 1: if(last_ticks < 250 || last_ticks >= 0) lights_pending[LightRecord] = true; break;
|
||||
default: if(last_ticks < 250) lights_pending[LightAnysolo] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::show_transport_time ()
|
||||
{
|
||||
nframes_t where = session->transport_frame();
|
||||
show_bbt(where);
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::show_smpte (nframes_t where)
|
||||
{
|
||||
if ((where != last_where) || lcd_isdamaged(1,9,10)) {
|
||||
|
||||
char buf[5];
|
||||
SMPTE::Time smpte;
|
||||
|
||||
session->smpte_time (where, smpte);
|
||||
|
||||
if (smpte.negative) {
|
||||
sprintf (buf, "-%02" PRIu32 ":", smpte.hours);
|
||||
} else {
|
||||
sprintf (buf, " %02" PRIu32 ":", smpte.hours);
|
||||
}
|
||||
print (1, 8, buf);
|
||||
|
||||
sprintf (buf, "%02" PRIu32 ":", smpte.minutes);
|
||||
print (1, 12, buf);
|
||||
|
||||
sprintf (buf, "%02" PRIu32 ":", smpte.seconds);
|
||||
print (1, 15, buf);
|
||||
|
||||
sprintf (buf, "%02" PRIu32, smpte.frames);
|
||||
print_noretry (1, 18, buf);
|
||||
|
||||
last_where = where;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TranzportControlProtocol::show_track_gain ()
|
||||
{
|
||||
// FIXME last_track gain has to become meter/track specific
|
||||
if (route_table[0]) {
|
||||
gain_t g = route_get_gain (0);
|
||||
if ((g != last_track_gain) || lcd_isdamaged(0,12,8)) {
|
||||
char buf[16];
|
||||
snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
|
||||
print (0, 12, buf);
|
||||
last_track_gain = g;
|
||||
}
|
||||
} else {
|
||||
print (0, 9, " ");
|
||||
}
|
||||
}
|
||||
40
libs/surfaces/tranzport/slider_gain.h
Normal file
40
libs/surfaces/tranzport/slider_gain.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
#ifndef ardour_slider_gain
|
||||
#define ardour_slider_gain
|
||||
|
||||
static inline double
|
||||
gain_to_slider_position (ARDOUR::gain_t g)
|
||||
{
|
||||
if (g == 0) return 0;
|
||||
return pow((6.0*log(g)/log(2.0)+192.0)/198.0, 8.0);
|
||||
|
||||
}
|
||||
|
||||
static inline ARDOUR::gain_t
|
||||
slider_position_to_gain (double pos)
|
||||
{
|
||||
/* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
|
||||
if (pos == 0.0) return 0;
|
||||
return pow (2.0,(sqrt(sqrt(sqrt(pos)))*198.0-192.0)/6.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
148
libs/surfaces/tranzport/state.cc
Normal file
148
libs/surfaces/tranzport/state.cc
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/audio_track.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/location.h>
|
||||
#include <ardour/dB.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace sigc;
|
||||
using namespace PBD;
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
#include <pbd/abstract_ui.cc>
|
||||
|
||||
#include "tranzport_control_protocol.h"
|
||||
|
||||
|
||||
// FIXME: How to handle multiple tranzports in a system?
|
||||
|
||||
XMLNode&
|
||||
TranzportControlProtocol::get_state ()
|
||||
{
|
||||
XMLNode* node = new XMLNode (X_("Protocol"));
|
||||
node->add_property (X_("name"), _name);
|
||||
return *node;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::set_state (const XMLNode& node)
|
||||
{
|
||||
cout << "TranzportControlProtocol::set_state: active " << _active << endl;
|
||||
int retval = 0;
|
||||
|
||||
// I think I want to make these strings rather than numbers
|
||||
#if 0
|
||||
// fetch current display mode
|
||||
if ( node.property( X_("display_mode") ) != 0 )
|
||||
{
|
||||
string display = node.property( X_("display_mode") )->value();
|
||||
try
|
||||
{
|
||||
set_active( true );
|
||||
int32_t new_display = atoi( display.c_str() );
|
||||
if ( display_mode != new_display ) display_mode = (DisplayMode)new_display;
|
||||
}
|
||||
catch ( exception & e )
|
||||
{
|
||||
cout << "exception in TranzportControlProtocol::set_state: " << e.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( node.property( X_("wheel_mode") ) != 0 )
|
||||
{
|
||||
string wheel = node.property( X_("wheel_mode") )->value();
|
||||
try
|
||||
{
|
||||
int32_t new_wheel = atoi( wheel.c_str() );
|
||||
if ( wheel_mode != new_wheel ) wheel_mode = (WheelMode) new_wheel;
|
||||
}
|
||||
catch ( exception & e )
|
||||
{
|
||||
cout << "exception in TranzportControlProtocol::set_state: " << e.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// fetch current bling mode
|
||||
if ( node.property( X_("bling") ) != 0 )
|
||||
{
|
||||
string bling = node.property( X_("bling_mode") )->value();
|
||||
try
|
||||
{
|
||||
int32_t new_bling = atoi( bling.c_str() );
|
||||
if ( bling_mode != new_bling ) bling_mode = (BlingMode) new_bling;
|
||||
}
|
||||
catch ( exception & e )
|
||||
{
|
||||
cout << "exception in TranzportControlProtocol::set_state: " << e.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
// These are intended for the day we have more options for tranzport modes
|
||||
// And perhaps we could load up sessions this way, too
|
||||
|
||||
int
|
||||
TranzportControlProtocol::save (char *name)
|
||||
{
|
||||
// Presently unimplemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::load (char *name)
|
||||
{
|
||||
// Presently unimplemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::save_config (char *name)
|
||||
{
|
||||
// Presently unimplemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TranzportControlProtocol::load_config (char *name)
|
||||
{
|
||||
// Presently unimplemented
|
||||
return 0;
|
||||
}
|
||||
78
libs/surfaces/tranzport/tranzport_base.h
Normal file
78
libs/surfaces/tranzport/tranzport_base.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* This header file is basically where all the tranzport debuggable options go.
|
||||
Try to only check it in with minimal debugging enabled so production
|
||||
systems don't have to fiddle with it. */
|
||||
|
||||
/* Design notes: The tranzport is a unique device, basically a
|
||||
20x2 character lcd gui with (almost) 22 shift keys and 8 blinking lights.
|
||||
|
||||
As such it has several unique constraints. In the libusb driver,
|
||||
the device exerts flow control
|
||||
by having a usb write fail. It is pointless to retry madly at that point,
|
||||
the device is busy, and it's not going to become unbusy very quickly.
|
||||
|
||||
So writes need to be either "mandatory" or "unreliable", and therein
|
||||
lies the rub, as the kernel can also drop writes, and missing an
|
||||
interrupt in userspace is also generally bad.
|
||||
|
||||
However, the kernel driver retries writes for you and also buffers and
|
||||
compresses incoming wheel events - it will rarely, if ever, drop data.
|
||||
|
||||
A more complex surface might have hundreds of lights and several displays.
|
||||
|
||||
mike@taht.net
|
||||
*/
|
||||
|
||||
#ifndef ardour_tranzport_base
|
||||
#define ardour_tranzport_base
|
||||
|
||||
#define DEFAULT_USB_TIMEOUT 10
|
||||
#define MAX_RETRY 1
|
||||
#define MAX_TRANZPORT_INFLIGHT 4
|
||||
#define DEBUG_TRANZPORT 0
|
||||
#define TRANZPORT_THREADS 0
|
||||
|
||||
#ifndef HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
#define HAVE_TRANZPORT_KERNEL_DRIVER 0
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_TRANZPORT_MIDI_DRIVER
|
||||
#define HAVE_TRANZPORT_MIDI_DRIVER 0
|
||||
#endif
|
||||
|
||||
// for now, this is what the device is called
|
||||
#define TRANZPORT_DEVICE "/dev/tranzport0"
|
||||
|
||||
#if DEBUG_TRANZPORT > 0
|
||||
#define DEBUG_TRANZPORT_SCREEN 10
|
||||
#define DEBUG_TRANZPORT_BITS 10
|
||||
#define DEBUG_TRANZPORT_LIGHTS 10
|
||||
#define DEBUG_TRANZPORT_STATE 10
|
||||
#else
|
||||
#define DEBUG_TRANZPORT 0
|
||||
#define DEBUG_TRANZPORT_BITS 0
|
||||
#define DEBUG_TRANZPORT_SCREEN 0
|
||||
#define DEBUG_TRANZPORT_LIGHTS 0
|
||||
#define DEBUG_TRANZPORT_STATE 0
|
||||
#endif
|
||||
#endif /* ardour_tranzport_base */
|
||||
|
||||
44
libs/surfaces/tranzport/tranzport_common.h
Normal file
44
libs/surfaces/tranzport/tranzport_common.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
/* The most common header files that the tranzport uses */
|
||||
|
||||
#ifndef ardour_tranzport_common
|
||||
#define ardour_tranzport_common
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pbd/pthread_utils.h>
|
||||
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/audio_track.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/tempo.h>
|
||||
#include <ardour/location.h>
|
||||
#include <ardour/dB.h>
|
||||
|
||||
#endif /* ardour_tranzport_common */
|
||||
|
|
@ -1,15 +1,40 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Copyright (C) 2007 Mike Taht
|
||||
|
||||
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ardour_tranzport_control_protocol_h
|
||||
#define ardour_tranzport_control_protocol_h
|
||||
|
||||
#include <vector>
|
||||
#include "tranzport_base.h"
|
||||
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#if !HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
#include <usb.h>
|
||||
#endif
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
|
||||
#include <control_protocol/control_protocol.h>
|
||||
|
|
@ -34,7 +59,12 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
static const int WRITE_ENDPOINT = 0x02;
|
||||
const static int STATUS_OFFLINE = 0xff;
|
||||
const static int STATUS_ONLINE = 0x01;
|
||||
const static uint8_t WheelDirectionThreshold = 0x3f;
|
||||
const static int STATUS_OK = 0x00;
|
||||
|
||||
const static int LIGHTS = 7;
|
||||
const static int ROWS = 2;
|
||||
const static int COLUMNS = 20;
|
||||
const static uint8_t WheelDirectionThreshold = 0x7f;
|
||||
|
||||
enum LightID {
|
||||
LightRecord = 0,
|
||||
|
|
@ -67,7 +97,8 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
ButtonStop = 0x00010000,
|
||||
ButtonPlay = 0x00100000,
|
||||
ButtonRecord = 0x00000100,
|
||||
ButtonShift = 0x08000000
|
||||
ButtonShift = 0x08000000,
|
||||
ButtonFootswitch = 0x00001000
|
||||
};
|
||||
|
||||
enum WheelShiftMode {
|
||||
|
|
@ -111,37 +142,55 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
BlingRotating,
|
||||
BlingPairs,
|
||||
BlingRows,
|
||||
BlingFlashAll
|
||||
BlingFlashAll,
|
||||
BlingEnter,
|
||||
BlingExit
|
||||
};
|
||||
|
||||
pthread_t thread;
|
||||
#if HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
int udev;
|
||||
#else
|
||||
usb_dev_handle* udev;
|
||||
#endif
|
||||
|
||||
#if TRANZPORT_THREADS
|
||||
pthread_t thread_read;
|
||||
#endif
|
||||
int last_read_error;
|
||||
|
||||
uint32_t buttonmask;
|
||||
uint32_t timeout;
|
||||
uint32_t inflight;
|
||||
uint32_t current_track_id;
|
||||
#if TRANZPORT_THREADS
|
||||
pthread_t thread_write;
|
||||
#endif
|
||||
int last_write_error;
|
||||
uint8_t _datawheel;
|
||||
uint8_t _device_status;
|
||||
uint32_t current_track_id;
|
||||
WheelMode wheel_mode;
|
||||
WheelShiftMode wheel_shift_mode;
|
||||
DisplayMode display_mode;
|
||||
BlingMode bling_mode;
|
||||
WheelIncrement wheel_increment;
|
||||
usb_dev_handle* udev;
|
||||
|
||||
ARDOUR::gain_t gain_fraction;
|
||||
|
||||
Glib::Mutex update_lock;
|
||||
|
||||
bool screen_invalid[2][20];
|
||||
char screen_current[2][20];
|
||||
char screen_pending[2][20];
|
||||
char screen_flash[2][20];
|
||||
std::bitset<ROWS*COLUMNS> screen_invalid;
|
||||
char screen_current[ROWS][COLUMNS];
|
||||
char screen_pending[ROWS][COLUMNS];
|
||||
char screen_flash[ROWS][COLUMNS];
|
||||
|
||||
bool lights_invalid[7];
|
||||
bool lights_current[7];
|
||||
bool lights_pending[7];
|
||||
bool lights_flash[7];
|
||||
std::bitset<LIGHTS> lights_invalid;
|
||||
std::bitset<LIGHTS> lights_current;
|
||||
std::bitset<LIGHTS> lights_pending;
|
||||
std::bitset<LIGHTS> lights_flash;
|
||||
|
||||
int32_t last_notify;
|
||||
char last_notify_msg[COLUMNS+1];
|
||||
uint32_t last_bars;
|
||||
uint32_t last_beats;
|
||||
uint32_t last_ticks;
|
||||
|
|
@ -164,16 +213,25 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
int write (uint8_t* cmd, uint32_t timeout_override = 0);
|
||||
int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
|
||||
int close ();
|
||||
int save(char *name = "default");
|
||||
int load(char *name = "default");
|
||||
int save_config(char *name = "default");
|
||||
int load_config(char *name = "default");
|
||||
int save(char *name);
|
||||
int load(char *name);
|
||||
void print (int row, int col, const char* text);
|
||||
void print_noretry (int row, int col, const char* text);
|
||||
void notify(const char *msg);
|
||||
|
||||
#if HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
int rtpriority_set(int priority = 3); // we don't need serious rt privs anymore
|
||||
#else
|
||||
int rtpriority_set(int priority = 52);
|
||||
#endif
|
||||
int rtpriority_unset(int priority = 0);
|
||||
|
||||
// I hate changing the api to do either but until I have clean io class what can you do?
|
||||
#if !HAVE_TRANZPORT_KERNEL_DRIVER
|
||||
int open_core (struct usb_device*);
|
||||
|
||||
#endif
|
||||
static void* _monitor_work (void* arg);
|
||||
void* monitor_work ();
|
||||
|
||||
|
|
@ -191,6 +249,7 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
int screen_flush();
|
||||
void screen_clear();
|
||||
// bool screen_isuptodate(); // think on this -
|
||||
int screen_show_bling();
|
||||
|
||||
// Commands to write to the lcd
|
||||
|
||||
|
|
@ -198,8 +257,8 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
bool lcd_damage();
|
||||
bool lcd_isdamaged();
|
||||
|
||||
bool lcd_damage(int row, int col = 0, int length = 20);
|
||||
bool lcd_isdamaged(int row, int col = 0, int length = 20);
|
||||
bool lcd_damage(int row, int col = 0, int length = COLUMNS);
|
||||
bool lcd_isdamaged(int row, int col = 0, int length = COLUMNS);
|
||||
|
||||
int lcd_flush();
|
||||
int lcd_write(uint8_t* cmd, uint32_t timeout_override = 0); // pedantic alias for write
|
||||
|
|
@ -240,6 +299,9 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
void enter_recording_mode();
|
||||
void enter_bling_mode();
|
||||
|
||||
void next_marker (); // basicui doesn't give me enough info
|
||||
void prev_marker ();
|
||||
|
||||
void next_display_mode ();
|
||||
void normal_update ();
|
||||
|
||||
|
|
@ -252,6 +314,9 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
void show_gain ();
|
||||
void show_pan ();
|
||||
void show_meter ();
|
||||
void show_mini_meter ();
|
||||
void show_bling();
|
||||
void show_notify();
|
||||
|
||||
void datawheel ();
|
||||
void scrub ();
|
||||
|
|
@ -311,8 +376,10 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol
|
|||
void button_event_play_release (bool shifted);
|
||||
void button_event_record_press (bool shifted);
|
||||
void button_event_record_release (bool shifted);
|
||||
void button_event_footswitch_press(bool shifted);
|
||||
void button_event_footswitch_release (bool shifted);
|
||||
|
||||
// new api
|
||||
// new api - still thinking about it
|
||||
void button_event_mute (bool pressed, bool shifted);
|
||||
};
|
||||
|
||||
|
|
|
|||
21
libs/surfaces/tranzport/view_automation.cc
Normal file
21
libs/surfaces/tranzport/view_automation.cc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2006 Paul Davis
|
||||
* Copyright (C) 2007 Michael Taht
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue