"merge" (i.e. wholesale import) 2.0-ongoing Mackie code and then fix to compile in...
authorPaul Davis <paul@linuxaudiosystems.com>
Fri, 12 Dec 2008 22:55:03 +0000 (22:55 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Fri, 12 Dec 2008 22:55:03 +0000 (22:55 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@4315 d708f5d6-7413-0410-9779-e7cbd77b26cf

43 files changed:
libs/surfaces/mackie/README [new file with mode: 0644]
libs/surfaces/mackie/SConscript
libs/surfaces/mackie/TODO
libs/surfaces/mackie/bcf_surface.cc
libs/surfaces/mackie/bcf_surface.h
libs/surfaces/mackie/bcf_surface_generated.cc [new file with mode: 0644]
libs/surfaces/mackie/controls.cc
libs/surfaces/mackie/controls.h
libs/surfaces/mackie/dummy_port.cc [new file with mode: 0644]
libs/surfaces/mackie/dummy_port.h [new file with mode: 0644]
libs/surfaces/mackie/interface.cc
libs/surfaces/mackie/mackie_button_handler.cc
libs/surfaces/mackie/mackie_button_handler.h
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.h
libs/surfaces/mackie/mackie_control_protocol_poll.cc
libs/surfaces/mackie/mackie_jog_wheel.cc [new file with mode: 0644]
libs/surfaces/mackie/mackie_jog_wheel.h [new file with mode: 0644]
libs/surfaces/mackie/mackie_midi_builder.cc
libs/surfaces/mackie/mackie_midi_builder.h
libs/surfaces/mackie/mackie_port.cc
libs/surfaces/mackie/mackie_port.h
libs/surfaces/mackie/mackie_surface.cc
libs/surfaces/mackie/mackie_surface.h
libs/surfaces/mackie/mackie_surface_generated.cc [new file with mode: 0644]
libs/surfaces/mackie/midi_byte_array.cc
libs/surfaces/mackie/midi_byte_array.h
libs/surfaces/mackie/route_signal.cc
libs/surfaces/mackie/route_signal.h
libs/surfaces/mackie/scripts/bcf-controls.csv
libs/surfaces/mackie/scripts/controls.rb
libs/surfaces/mackie/scripts/host.rb
libs/surfaces/mackie/scripts/mackie-dump.midi [new file with mode: 0644]
libs/surfaces/mackie/scripts/simple_host.rb
libs/surfaces/mackie/scripts/surface-cc-template.erb
libs/surfaces/mackie/scripts/test_controls.rb
libs/surfaces/mackie/surface.cc
libs/surfaces/mackie/surface.h
libs/surfaces/mackie/surface_port.cc
libs/surfaces/mackie/surface_port.h
libs/surfaces/mackie/timer.h [new file with mode: 0644]
libs/surfaces/mackie/types.cc
libs/surfaces/mackie/types.h

diff --git a/libs/surfaces/mackie/README b/libs/surfaces/mackie/README
new file mode 100644 (file)
index 0000000..5ed3a75
--- /dev/null
@@ -0,0 +1,18 @@
+For usage, see
+
+http://www.ardour.org/files/manual/sn-mackie.html
+
+unfortunately it's a bit outdated, so to get the latest manual, go to the manual
+directory in the ardour source tree, and say "make html" to build the latest.
+Then point your favourite browser at tmp/index.html
+
+NOTES:
+
+* support for alsa/sequencer ports is currently broken. We're working on it.
+
+* you'll need to make port changes in etc/ardour2/ardour_system.rc and
+etc/ardour2/ardour.rc, otherwise they don't stay changed in ~/.ardour2/ardour.rc
+
+
+John Anderson
+panic@semiosix.com
index c19d145c73e4804d9a91129f7854adeee6ddf977..96b37c2d6e3a437f1161590004994cd47a784720 100644 (file)
@@ -14,12 +14,16 @@ mackie = env.Clone()
 
 domain = 'ardour_mackie'
 
-mackie.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
+mackie.Append(DOMAIN = domain, MAJOR = 1, MINOR = 1, MICRO = 0)
 mackie.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
 mackie.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
 mackie.Append(PACKAGE = domain)
 mackie.Append(POTFILE = domain + '.pot')
 
+if mackie['DEBUG'] == 1:
+  mackie.Append(CXXFLAGS="-DDEBUG")
+  mackie.Append(CXXFLAGS="-DPORT_DEBUG")
+
 if mackie['IS_OSX']:
     mackie.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
 
@@ -27,17 +31,21 @@ mackie_files=Split("""
 interface.cc
 midi_byte_array.cc
 controls.cc
+surface_port.cc
+dummy_port.cc
+mackie_port.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
+bcf_surface_generated.cc
 mackie_surface.cc
+mackie_surface_generated.cc
+mackie_jog_wheel.cc
 """)
 
 mackie.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
@@ -53,7 +61,6 @@ mackie.Merge ([
     libraries['sigc2'],
     libraries['pbd'],
     libraries['midi++2'],
-    libraries['evoral'],
     libraries['xml'],
     libraries['glib2'],
     libraries['glibmm2'],
@@ -72,7 +79,7 @@ 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'], 'ardour3','surfaces'), libardour_mackie))
+       env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2','surfaces'), libardour_mackie))
 
 env.Alias('tarball', env.Distribute (env['DISTTREE'],
                                     [ 'SConscript' ] +
index a9cb1b987870369135deb12220aa881ef88db2c3..2573e0f05b7d734921dcde972b0ae790f4945641 100644 (file)
@@ -1,20 +1,48 @@
-* 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.
-
-* jog with transport rolling doesn't work properly. My use of ScrollTimeline also doesn't work.
-* make loop button sensitive to current transport state
-* make sure rew button can go past the previous if pressed twice, relatively quickly.
-* finish button mapping. Only shifted buttons left for bcf.
+* implement handle_port_inactive properly
+* two bcf doesn't work
+* remappable buttons (OSC or Surfax?)
+* 7/1 configurable to 8
+* need an object that can encapsulate different port types, ie BCF vs MCU. Not at the surface level.
+* finish button implementations.
 * concurrency for bank switching? And make sure "old" events aren't sent to "new" faders
 * TODOs in code
 * removal of a route results in a strip that isn't dead, but doesn't have any effect on the session
 * use i18n. see string_compose
-* docs in manual, including button assignment diagram
+
+MCU
+---
+* if mackie wheel moves too fast, it's ignored.
+* gain/panner display in second line
+* metering on second line
+* per-strip signal led
+* midi bandwidth?
+* Zoom buttons, from Jean-Martin Barbut. In fact I'm a bit desappointed with those functions, because I was used
+       to use the 4 "N-E-S-W" buttons to zoom in and out with horizontal
+       buttons and zoom in/out on track height with vertical buttons. That is
+       with the zoom button in. with the zoom button off, it selects tracks
+       with vertical buttons and selects regions with horizontal buttons. This
+       was quite useful. The fact that the zoom button switches the wheel to
+       zoom is fine, but it would be great to also have the track/region
+       selection and the track height available from those buttons. I.e : you
+       select a track with the vertical arrows and you increase/decrease the
+       eight of the track with the zoom btn on. This combined with the region
+       selection allows to zoom in a region (vertical and horizontal zoom) very
+       easily.
+
 
 Later
 -----
+* 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.
+* alsa/sequencer ports unstable. possibly problems with use of ::poll
+* use glib::Timer instead of mine. Actually don't because it's not very useable.
+* crash when mmc port set to mcu?
 * remove commented couts
+* Perhaps MackieControlProtocol shouldn't implement MackieButtonHandler
+* Need a HostAdapter class to encapsulate ardour calls
+* talk to route plugins
+* check for excessiveness (ie too many events making other subsystems work too hard)
 * Queueing of writes?
 * Generic surface code to common location
 * bulk remote id changes cause too many surface updates. use Config->remote_model.
@@ -25,21 +53,16 @@ Later
 * mix busses and/or a "bus-only" bank/mode
 * what about surfaces like Mackie C4 and BCR2000?
 
-Need UI integration
--------------------
+UI integration
+--------------
+
+* maybe use current snap state for jog wheel and ffwd/rew
 * Some indication on the UI of currently bank-switched-in routes?
   Useful for surfaces that don't have a scribble strip.
-* use current zoom setting and snap state for shuttle wheel
-
-Actual Mackie
--------------
-* docs claim that unit will send a host query on init.
-* test Mackie surface object. Apparently led rings don't work. Stereo busses?
-* timecode & 55 char displays
-* midi bandwidth
 
 Bugs
 ----
-
-* definitely something wrong with remote_id assignment on session create
-  (master strip assigned 0).
+* when using alsa/sequencer, some midi events intended for mcu port end up being
+  read by the seq port.
+* MIDI::Port::type() returns _type, which is undefined. It might be in the descriptor
+* auditioner doesn't connect to master 1 and master 2 - it loses the spaces.
index 45b5ad85fa7cecf0f8fd1b51f968fecd36489086..0898e95ba1b2c884fd52152537fc2908fa9f455e 100644 (file)
-/*
-       Generated by scripts/generate-surface.rb
-*/
-
 #include "bcf_surface.h"
+#include "surface_port.h"
+#include "mackie_midi_builder.h"
 
-#include "controls.h"
-#include "mackie_button_handler.h"
+#include <cmath>
 
 using namespace Mackie;
 
-void Mackie::BcfSurface::init_controls()
+void BcfSurface::display_bank_start( SurfacePort & port, MackieMidiBuilder & builder, uint32_t current_bank )
 {
-       // intialise groups and strips
-       Group * group = 0;
-       
-       // make sure there are enough strips
-       strips.resize( 7 );
-       
-       group = new Group ( "user" );
-       groups["user"] = group;
-       
-       group = new Group ( "assignment" );
-       groups["assignment"] = group;
-       
-       group = new Group ( "none" );
-       groups["none"] = group;
-       
-       group = new MasterStrip ( "master", 0 );
-       groups["master"] = group;
-       strips[0] = dynamic_cast<Strip*>( group );
-       
-       group = new Strip ( "strip_1", 0 );
-       groups["strip_1"] = group;
-       strips[0] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "cursor" );
-       groups["cursor"] = group;
-       
-       group = new Strip ( "strip_2", 1 );
-       groups["strip_2"] = group;
-       strips[1] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "functions" );
-       groups["functions"] = group;
-       
-       group = new Group ( "automation" );
-       groups["automation"] = group;
-       
-       group = new Strip ( "strip_3", 2 );
-       groups["strip_3"] = group;
-       strips[2] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "display" );
-       groups["display"] = group;
-       
-       group = new Strip ( "strip_4", 3 );
-       groups["strip_4"] = group;
-       strips[3] = dynamic_cast<Strip*>( group );
-       
-       group = new Strip ( "strip_5", 4 );
-       groups["strip_5"] = group;
-       strips[4] = dynamic_cast<Strip*>( group );
-       
-       group = new Strip ( "strip_6", 5 );
-       groups["strip_6"] = group;
-       strips[5] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "transport" );
-       groups["transport"] = group;
-       
-       group = new Strip ( "strip_7", 6 );
-       groups["strip_7"] = group;
-       strips[6] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "modifiers" );
-       groups["modifiers"] = group;
-       
-       group = new Group ( "bank" );
-       groups["bank"] = group;
-       
-
-       // initialise controls
-       Control * control = 0;
-
-       group = groups["strip_1"];
-       control = new Fader ( 0, 1, "gain", *group );
-       faders[0x00] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Fader ( 1, 2, "gain", *group );
-       faders[0x01] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Fader ( 2, 3, "gain", *group );
-       faders[0x02] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Fader ( 3, 4, "gain", *group );
-       faders[0x03] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Fader ( 4, 5, "gain", *group );
-       faders[0x04] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Fader ( 5, 6, "gain", *group );
-       faders[0x05] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Fader ( 6, 7, "gain", *group );
-       faders[0x06] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["master"];
-       control = new Fader ( 7, 1, "gain", *group );
-       faders[0x07] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Pot ( 16, 1, "vpot", *group );
-       pots[0x10] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Pot ( 17, 2, "vpot", *group );
-       pots[0x11] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Pot ( 18, 3, "vpot", *group );
-       pots[0x12] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Pot ( 19, 4, "vpot", *group );
-       pots[0x13] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Pot ( 20, 5, "vpot", *group );
-       pots[0x14] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Pot ( 21, 6, "vpot", *group );
-       pots[0x15] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Pot ( 22, 7, "vpot", *group );
-       pots[0x16] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Pot ( 23, 1, "jog", *group );
-       pots[0x17] = control;
-       controls.push_back( control );
-       controls_by_name["jog"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Pot ( 46, 1, "external", *group );
-       pots[0x2e] = control;
-       controls.push_back( control );
-       controls_by_name["external"] = control;
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 24, 1, "recenable", *group );
-       buttons[0x18] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 25, 2, "recenable", *group );
-       buttons[0x19] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 26, 3, "recenable", *group );
-       buttons[0x1a] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 27, 4, "recenable", *group );
-       buttons[0x1b] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 28, 5, "recenable", *group );
-       buttons[0x1c] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 29, 6, "recenable", *group );
-       buttons[0x1d] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 30, 7, "recenable", *group );
-       buttons[0x1e] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 32, 1, "solo", *group );
-       buttons[0x20] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 33, 2, "solo", *group );
-       buttons[0x21] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 34, 3, "solo", *group );
-       buttons[0x22] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 35, 4, "solo", *group );
-       buttons[0x23] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 36, 5, "solo", *group );
-       buttons[0x24] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 37, 6, "solo", *group );
-       buttons[0x25] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 38, 7, "solo", *group );
-       buttons[0x26] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 16, 1, "mute", *group );
-       buttons[0x10] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 17, 2, "mute", *group );
-       buttons[0x11] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 18, 3, "mute", *group );
-       buttons[0x12] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 19, 4, "mute", *group );
-       buttons[0x13] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 20, 5, "mute", *group );
-       buttons[0x14] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 21, 6, "mute", *group );
-       buttons[0x15] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 22, 7, "mute", *group );
-       buttons[0x16] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 0, 1, "select", *group );
-       buttons[0x00] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 1, 2, "select", *group );
-       buttons[0x01] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 2, 3, "select", *group );
-       buttons[0x02] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 3, 4, "select", *group );
-       buttons[0x03] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 4, 5, "select", *group );
-       buttons[0x04] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 5, 6, "select", *group );
-       buttons[0x05] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 6, 7, "select", *group );
-       buttons[0x06] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 8, 1, "vselect", *group );
-       buttons[0x08] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 9, 2, "vselect", *group );
-       buttons[0x09] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 10, 3, "vselect", *group );
-       buttons[0x0a] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 11, 4, "vselect", *group );
-       buttons[0x0b] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 12, 5, "vselect", *group );
-       buttons[0x0c] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 13, 6, "vselect", *group );
-       buttons[0x0d] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 14, 7, "vselect", *group );
-       buttons[0x0e] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 40, 1, "io", *group );
-       buttons[0x28] = control;
-       controls.push_back( control );
-       controls_by_name["io"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 90, 1, "sends", *group );
-       buttons[0x5a] = control;
-       controls.push_back( control );
-       controls_by_name["sends"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 89, 1, "pan", *group );
-       buttons[0x59] = control;
-       controls.push_back( control );
-       controls_by_name["pan"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 87, 1, "plugin", *group );
-       buttons[0x57] = control;
-       controls.push_back( control );
-       controls_by_name["plugin"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 88, 1, "eq", *group );
-       buttons[0x58] = control;
-       controls.push_back( control );
-       controls_by_name["eq"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 45, 1, "dyn", *group );
-       buttons[0x2d] = control;
-       controls.push_back( control );
-       controls_by_name["dyn"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 46, 1, "left", *group );
-       buttons[0x2e] = control;
-       controls.push_back( control );
-       controls_by_name["left"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 47, 1, "right", *group );
-       buttons[0x2f] = control;
-       controls.push_back( control );
-       controls_by_name["right"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 48, 1, "channel_left", *group );
-       buttons[0x30] = control;
-       controls.push_back( control );
-       controls_by_name["channel_left"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 49, 1, "channel_right", *group );
-       buttons[0x31] = control;
-       controls.push_back( control );
-       controls_by_name["channel_right"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 50, 1, "flip", *group );
-       buttons[0x32] = control;
-       controls.push_back( control );
-       controls_by_name["flip"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 86, 1, "edit", *group );
-       buttons[0x56] = control;
-       controls.push_back( control );
-       controls_by_name["edit"] = control;
-       group->add( *control );
-
-       group = groups["display"];
-       control = new Button ( 52, 1, "name_value", *group );
-       buttons[0x34] = control;
-       controls.push_back( control );
-       controls_by_name["name_value"] = control;
-       group->add( *control );
-
-       group = groups["display"];
-       control = new Button ( 53, 1, "smpte_beats", *group );
-       buttons[0x35] = control;
-       controls.push_back( control );
-       controls_by_name["smpte_beats"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 54, 1, "F1", *group );
-       buttons[0x36] = control;
-       controls.push_back( control );
-       controls_by_name["F1"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 55, 1, "F2", *group );
-       buttons[0x37] = control;
-       controls.push_back( control );
-       controls_by_name["F2"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 56, 1, "F3", *group );
-       buttons[0x38] = control;
-       controls.push_back( control );
-       controls_by_name["F3"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 57, 1, "F4", *group );
-       buttons[0x39] = control;
-       controls.push_back( control );
-       controls_by_name["F4"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 58, 1, "F5", *group );
-       buttons[0x3a] = control;
-       controls.push_back( control );
-       controls_by_name["F5"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 59, 1, "F6", *group );
-       buttons[0x3b] = control;
-       controls.push_back( control );
-       controls_by_name["F6"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 60, 1, "F7", *group );
-       buttons[0x3c] = control;
-       controls.push_back( control );
-       controls_by_name["F7"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 61, 1, "F8", *group );
-       buttons[0x3d] = control;
-       controls.push_back( control );
-       controls_by_name["F8"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 62, 1, "F9", *group );
-       buttons[0x3e] = control;
-       controls.push_back( control );
-       controls_by_name["F9"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 63, 1, "F10", *group );
-       buttons[0x3f] = control;
-       controls.push_back( control );
-       controls_by_name["F10"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 64, 1, "F11", *group );
-       buttons[0x40] = control;
-       controls.push_back( control );
-       controls_by_name["F11"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 65, 1, "F12", *group );
-       buttons[0x41] = control;
-       controls.push_back( control );
-       controls_by_name["F12"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 66, 1, "F13", *group );
-       buttons[0x42] = control;
-       controls.push_back( control );
-       controls_by_name["F13"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 67, 1, "F14", *group );
-       buttons[0x43] = control;
-       controls.push_back( control );
-       controls_by_name["F14"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 68, 1, "F15", *group );
-       buttons[0x44] = control;
-       controls.push_back( control );
-       controls_by_name["F15"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 69, 1, "F16", *group );
-       buttons[0x45] = control;
-       controls.push_back( control );
-       controls_by_name["F16"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 39, 1, "global_solo", *group );
-       buttons[0x27] = control;
-       controls.push_back( control );
-       controls_by_name["global_solo"] = control;
-       group->add( *control );
-
-       group = groups["modifiers"];
-       control = new Button ( 80, 1, "option", *group );
-       buttons[0x50] = control;
-       controls.push_back( control );
-       controls_by_name["option"] = control;
-       group->add( *control );
-
-       group = groups["modifiers"];
-       control = new Button ( 73, 1, "cmd_alt", *group );
-       buttons[0x49] = control;
-       controls.push_back( control );
-       controls_by_name["cmd_alt"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 74, 1, "on", *group );
-       buttons[0x4a] = control;
-       controls.push_back( control );
-       controls_by_name["on"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 75, 1, "rec_ready", *group );
-       buttons[0x4b] = control;
-       controls.push_back( control );
-       controls_by_name["rec_ready"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 76, 1, "undo", *group );
-       buttons[0x4c] = control;
-       controls.push_back( control );
-       controls_by_name["undo"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 77, 1, "snapshot", *group );
-       buttons[0x4d] = control;
-       controls.push_back( control );
-       controls_by_name["snapshot"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 79, 1, "redo", *group );
-       buttons[0x4f] = control;
-       controls.push_back( control );
-       controls_by_name["redo"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 71, 1, "marker", *group );
-       buttons[0x47] = control;
-       controls.push_back( control );
-       controls_by_name["marker"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 81, 1, "enter", *group );
-       buttons[0x51] = control;
-       controls.push_back( control );
-       controls_by_name["enter"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 82, 1, "cancel", *group );
-       buttons[0x52] = control;
-       controls.push_back( control );
-       controls_by_name["cancel"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 83, 1, "mixer", *group );
-       buttons[0x53] = control;
-       controls.push_back( control );
-       controls_by_name["mixer"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 91, 1, "frm_left", *group );
-       buttons[0x5b] = control;
-       controls.push_back( control );
-       controls_by_name["frm_left"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 92, 1, "frm_right", *group );
-       buttons[0x5c] = control;
-       controls.push_back( control );
-       controls_by_name["frm_right"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 70, 1, "loop", *group );
-       buttons[0x46] = control;
-       controls.push_back( control );
-       controls_by_name["loop"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 72, 1, "punch_in", *group );
-       buttons[0x48] = control;
-       controls.push_back( control );
-       controls_by_name["punch_in"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 78, 1, "punch_out", *group );
-       buttons[0x4e] = control;
-       controls.push_back( control );
-       controls_by_name["punch_out"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 42, 1, "home", *group );
-       buttons[0x2a] = control;
-       controls.push_back( control );
-       controls_by_name["home"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 41, 1, "end", *group );
-       buttons[0x29] = control;
-       controls.push_back( control );
-       controls_by_name["end"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 44, 1, "rewind", *group );
-       buttons[0x2c] = control;
-       controls.push_back( control );
-       controls_by_name["rewind"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 43, 1, "ffwd", *group );
-       buttons[0x2b] = control;
-       controls.push_back( control );
-       controls_by_name["ffwd"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 93, 1, "stop", *group );
-       buttons[0x5d] = control;
-       controls.push_back( control );
-       controls_by_name["stop"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 94, 1, "play", *group );
-       buttons[0x5e] = control;
-       controls.push_back( control );
-       controls_by_name["play"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 31, 1, "record", *group );
-       buttons[0x1f] = control;
-       controls.push_back( control );
-       controls_by_name["record"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 96, 1, "cursor_up", *group );
-       buttons[0x60] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_up"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 97, 1, "cursor_down", *group );
-       buttons[0x61] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_down"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 98, 1, "cursor_left", *group );
-       buttons[0x62] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_left"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 99, 1, "cursor_right", *group );
-       buttons[0x63] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_right"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 100, 1, "zoom", *group );
-       buttons[0x64] = control;
-       controls.push_back( control );
-       controls_by_name["zoom"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 101, 1, "scrub", *group );
-       buttons[0x65] = control;
-       controls.push_back( control );
-       controls_by_name["scrub"] = control;
-       group->add( *control );
-
-       group = groups["user"];
-       control = new Button ( 102, 1, "user_a", *group );
-       buttons[0x66] = control;
-       controls.push_back( control );
-       controls_by_name["user_a"] = control;
-       group->add( *control );
-
-       group = groups["user"];
-       control = new Button ( 103, 1, "user_b", *group );
-       buttons[0x67] = control;
-       controls.push_back( control );
-       controls_by_name["user_b"] = control;
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 104, 1, "fader_touch", *group );
-       buttons[0x68] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 105, 2, "fader_touch", *group );
-       buttons[0x69] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 106, 3, "fader_touch", *group );
-       buttons[0x6a] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 107, 4, "fader_touch", *group );
-       buttons[0x6b] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 108, 5, "fader_touch", *group );
-       buttons[0x6c] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 109, 6, "fader_touch", *group );
-       buttons[0x6d] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 110, 7, "fader_touch", *group );
-       buttons[0x6e] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["master"];
-       control = new Button ( 111, 1, "fader_touch", *group );
-       buttons[0x6f] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["master"];
-       control = new Button ( 23, 1, "mute", *group );
-       buttons[0x17] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 51, 1, "clicking", *group );
-       buttons[0x33] = control;
-       controls.push_back( control );
-       controls_by_name["clicking"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 113, 1, "smpte", *group );
-       leds[0x71] = control;
-       controls.push_back( control );
-       controls_by_name["smpte"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 114, 1, "beats", *group );
-       leds[0x72] = control;
-       controls.push_back( control );
-       controls_by_name["beats"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 115, 1, "solo", *group );
-       leds[0x73] = control;
-       controls.push_back( control );
-       controls_by_name["solo"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 118, 1, "relay_click", *group );
-       leds[0x76] = control;
-       controls.push_back( control );
-       controls_by_name["relay_click"] = control;
-       group->add( *control );
-
-}
-
-void Mackie::BcfSurface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
-{
-       if ( bs != press && bs != release )
+       if ( current_bank == 0 )
        {
-               mbh.update_led( button, none );
-               return;
+               // send Ar. to 2-char display on the master
+               port.write( builder.two_char_display( "Ar", ".." ) );
        }
-       
-       LedState ls;
-       switch ( button.id() )
+       else
        {
+               // write the current first remote_id to the 2-char display
+               port.write( builder.two_char_display( current_bank ) );
+       }
+}
 
-               case 0x28: // io
-                       switch ( bs ) {
-                               case press: ls = mbh.io_press( button ); break;
-                               case release: ls = mbh.io_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5a: // sends
-                       switch ( bs ) {
-                               case press: ls = mbh.sends_press( button ); break;
-                               case release: ls = mbh.sends_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x59: // pan
-                       switch ( bs ) {
-                               case press: ls = mbh.pan_press( button ); break;
-                               case release: ls = mbh.pan_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x57: // plugin
-                       switch ( bs ) {
-                               case press: ls = mbh.plugin_press( button ); break;
-                               case release: ls = mbh.plugin_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x58: // eq
-                       switch ( bs ) {
-                               case press: ls = mbh.eq_press( button ); break;
-                               case release: ls = mbh.eq_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2d: // dyn
-                       switch ( bs ) {
-                               case press: ls = mbh.dyn_press( button ); break;
-                               case release: ls = mbh.dyn_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2e: // left
-                       switch ( bs ) {
-                               case press: ls = mbh.left_press( button ); break;
-                               case release: ls = mbh.left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2f: // right
-                       switch ( bs ) {
-                               case press: ls = mbh.right_press( button ); break;
-                               case release: ls = mbh.right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x30: // channel_left
-                       switch ( bs ) {
-                               case press: ls = mbh.channel_left_press( button ); break;
-                               case release: ls = mbh.channel_left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x31: // channel_right
-                       switch ( bs ) {
-                               case press: ls = mbh.channel_right_press( button ); break;
-                               case release: ls = mbh.channel_right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x32: // flip
-                       switch ( bs ) {
-                               case press: ls = mbh.flip_press( button ); break;
-                               case release: ls = mbh.flip_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x56: // edit
-                       switch ( bs ) {
-                               case press: ls = mbh.edit_press( button ); break;
-                               case release: ls = mbh.edit_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x34: // name_value
-                       switch ( bs ) {
-                               case press: ls = mbh.name_value_press( button ); break;
-                               case release: ls = mbh.name_value_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x35: // smpte_beats
-                       switch ( bs ) {
-                               case press: ls = mbh.smpte_beats_press( button ); break;
-                               case release: ls = mbh.smpte_beats_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x36: // F1
-                       switch ( bs ) {
-                               case press: ls = mbh.F1_press( button ); break;
-                               case release: ls = mbh.F1_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x37: // F2
-                       switch ( bs ) {
-                               case press: ls = mbh.F2_press( button ); break;
-                               case release: ls = mbh.F2_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x38: // F3
-                       switch ( bs ) {
-                               case press: ls = mbh.F3_press( button ); break;
-                               case release: ls = mbh.F3_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x39: // F4
-                       switch ( bs ) {
-                               case press: ls = mbh.F4_press( button ); break;
-                               case release: ls = mbh.F4_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3a: // F5
-                       switch ( bs ) {
-                               case press: ls = mbh.F5_press( button ); break;
-                               case release: ls = mbh.F5_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3b: // F6
-                       switch ( bs ) {
-                               case press: ls = mbh.F6_press( button ); break;
-                               case release: ls = mbh.F6_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3c: // F7
-                       switch ( bs ) {
-                               case press: ls = mbh.F7_press( button ); break;
-                               case release: ls = mbh.F7_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3d: // F8
-                       switch ( bs ) {
-                               case press: ls = mbh.F8_press( button ); break;
-                               case release: ls = mbh.F8_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3e: // F9
-                       switch ( bs ) {
-                               case press: ls = mbh.F9_press( button ); break;
-                               case release: ls = mbh.F9_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3f: // F10
-                       switch ( bs ) {
-                               case press: ls = mbh.F10_press( button ); break;
-                               case release: ls = mbh.F10_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x40: // F11
-                       switch ( bs ) {
-                               case press: ls = mbh.F11_press( button ); break;
-                               case release: ls = mbh.F11_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x41: // F12
-                       switch ( bs ) {
-                               case press: ls = mbh.F12_press( button ); break;
-                               case release: ls = mbh.F12_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x42: // F13
-                       switch ( bs ) {
-                               case press: ls = mbh.F13_press( button ); break;
-                               case release: ls = mbh.F13_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x43: // F14
-                       switch ( bs ) {
-                               case press: ls = mbh.F14_press( button ); break;
-                               case release: ls = mbh.F14_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x44: // F15
-                       switch ( bs ) {
-                               case press: ls = mbh.F15_press( button ); break;
-                               case release: ls = mbh.F15_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x45: // F16
-                       switch ( bs ) {
-                               case press: ls = mbh.F16_press( button ); break;
-                               case release: ls = mbh.F16_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x27: // global_solo
-                       switch ( bs ) {
-                               case press: ls = mbh.global_solo_press( button ); break;
-                               case release: ls = mbh.global_solo_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x50: // option
-                       switch ( bs ) {
-                               case press: ls = mbh.option_press( button ); break;
-                               case release: ls = mbh.option_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x49: // cmd_alt
-                       switch ( bs ) {
-                               case press: ls = mbh.cmd_alt_press( button ); break;
-                               case release: ls = mbh.cmd_alt_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4a: // on
-                       switch ( bs ) {
-                               case press: ls = mbh.on_press( button ); break;
-                               case release: ls = mbh.on_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4b: // rec_ready
-                       switch ( bs ) {
-                               case press: ls = mbh.rec_ready_press( button ); break;
-                               case release: ls = mbh.rec_ready_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4c: // undo
-                       switch ( bs ) {
-                               case press: ls = mbh.undo_press( button ); break;
-                               case release: ls = mbh.undo_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4d: // snapshot
-                       switch ( bs ) {
-                               case press: ls = mbh.snapshot_press( button ); break;
-                               case release: ls = mbh.snapshot_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4f: // redo
-                       switch ( bs ) {
-                               case press: ls = mbh.redo_press( button ); break;
-                               case release: ls = mbh.redo_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x47: // marker
-                       switch ( bs ) {
-                               case press: ls = mbh.marker_press( button ); break;
-                               case release: ls = mbh.marker_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x51: // enter
-                       switch ( bs ) {
-                               case press: ls = mbh.enter_press( button ); break;
-                               case release: ls = mbh.enter_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x52: // cancel
-                       switch ( bs ) {
-                               case press: ls = mbh.cancel_press( button ); break;
-                               case release: ls = mbh.cancel_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x53: // mixer
-                       switch ( bs ) {
-                               case press: ls = mbh.mixer_press( button ); break;
-                               case release: ls = mbh.mixer_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5b: // frm_left
-                       switch ( bs ) {
-                               case press: ls = mbh.frm_left_press( button ); break;
-                               case release: ls = mbh.frm_left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5c: // frm_right
-                       switch ( bs ) {
-                               case press: ls = mbh.frm_right_press( button ); break;
-                               case release: ls = mbh.frm_right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x46: // loop
-                       switch ( bs ) {
-                               case press: ls = mbh.loop_press( button ); break;
-                               case release: ls = mbh.loop_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x48: // punch_in
-                       switch ( bs ) {
-                               case press: ls = mbh.punch_in_press( button ); break;
-                               case release: ls = mbh.punch_in_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4e: // punch_out
-                       switch ( bs ) {
-                               case press: ls = mbh.punch_out_press( button ); break;
-                               case release: ls = mbh.punch_out_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2a: // home
-                       switch ( bs ) {
-                               case press: ls = mbh.home_press( button ); break;
-                               case release: ls = mbh.home_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x29: // end
-                       switch ( bs ) {
-                               case press: ls = mbh.end_press( button ); break;
-                               case release: ls = mbh.end_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2c: // rewind
-                       switch ( bs ) {
-                               case press: ls = mbh.rewind_press( button ); break;
-                               case release: ls = mbh.rewind_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2b: // ffwd
-                       switch ( bs ) {
-                               case press: ls = mbh.ffwd_press( button ); break;
-                               case release: ls = mbh.ffwd_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5d: // stop
-                       switch ( bs ) {
-                               case press: ls = mbh.stop_press( button ); break;
-                               case release: ls = mbh.stop_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5e: // play
-                       switch ( bs ) {
-                               case press: ls = mbh.play_press( button ); break;
-                               case release: ls = mbh.play_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x1f: // record
-                       switch ( bs ) {
-                               case press: ls = mbh.record_press( button ); break;
-                               case release: ls = mbh.record_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x60: // cursor_up
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_up_press( button ); break;
-                               case release: ls = mbh.cursor_up_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x61: // cursor_down
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_down_press( button ); break;
-                               case release: ls = mbh.cursor_down_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x62: // cursor_left
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_left_press( button ); break;
-                               case release: ls = mbh.cursor_left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x63: // cursor_right
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_right_press( button ); break;
-                               case release: ls = mbh.cursor_right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x64: // zoom
-                       switch ( bs ) {
-                               case press: ls = mbh.zoom_press( button ); break;
-                               case release: ls = mbh.zoom_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x65: // scrub
-                       switch ( bs ) {
-                               case press: ls = mbh.scrub_press( button ); break;
-                               case release: ls = mbh.scrub_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x66: // user_a
-                       switch ( bs ) {
-                               case press: ls = mbh.user_a_press( button ); break;
-                               case release: ls = mbh.user_a_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
+void BcfSurface::zero_all( SurfacePort & port, MackieMidiBuilder & builder )
+{
+       // clear 2-char display
+       port.write( builder.two_char_display( "LC" ) );
 
-               case 0x67: // user_b
-                       switch ( bs ) {
-                               case press: ls = mbh.user_b_press( button ); break;
-                               case release: ls = mbh.user_b_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
+       // and the led ring for the master strip
+       blank_jog_ring( port, builder );
+}
 
-               case 0x33: // clicking
-                       switch ( bs ) {
-                               case press: ls = mbh.clicking_press( button ); break;
-                               case release: ls = mbh.clicking_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
+void BcfSurface::blank_jog_ring( SurfacePort & port, MackieMidiBuilder & builder )
+{
+       Control & control = *controls_by_name["jog"];
+       port.write( builder.build_led_ring( dynamic_cast<Pot &>( control ), off ) );
+}
 
-       }
-       mbh.update_led( button, ls );
+float BcfSurface::scaled_delta( const ControlState & state, float current_speed )
+{
+       return state.sign * ( std::pow( float(state.ticks + 1), 2 ) + current_speed ) / 100.0;
 }
+
index a5fd3bf5a3ec46ccd99998f2a1bacc7758f9135b..95bb27db010e93bb5efb2da7db1ffd29bc03884c 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef mackie_surface_bcf_h
 #define mackie_surface_bcf_h
 /*
-       Generated by scripts/generate-surface.rb
+       Initially generated by scripts/generate-surface.rb
 */
 
 #include "surface.h"
@@ -20,6 +20,15 @@ public:
        
        virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
        virtual void init_controls();
+       
+       virtual void display_bank_start( SurfacePort & port, MackieMidiBuilder & builder, uint32_t current_bank );
+       virtual void zero_all( SurfacePort & port, MackieMidiBuilder & builder );
+       virtual void blank_jog_ring( SurfacePort & port, MackieMidiBuilder & builder );
+       virtual bool has_timecode_display() const { return false; }
+       
+       virtual float scrub_scaling_factor() { return 50.0; }
+       virtual float scaled_delta( const ControlState & state, float current_speed );
+
 };
 
 }
diff --git a/libs/surfaces/mackie/bcf_surface_generated.cc b/libs/surfaces/mackie/bcf_surface_generated.cc
new file mode 100644 (file)
index 0000000..f1d8d7a
--- /dev/null
@@ -0,0 +1,1461 @@
+/*
+       Generated by scripts/generate-surface.rb
+*/
+
+#include "bcf_surface.h"
+
+#include "controls.h"
+#include "mackie_button_handler.h"
+
+using namespace Mackie;
+
+void Mackie::BcfSurface::init_controls()
+{
+       // intialise groups and strips
+       Group * group = 0;
+       
+       // make sure there are enough strips
+       strips.resize( 7 );
+       
+       group = new Group ( "user" );
+       groups["user"] = group;
+       
+       group = new Group ( "assignment" );
+       groups["assignment"] = group;
+       
+       group = new Group ( "none" );
+       groups["none"] = group;
+       
+       group = new MasterStrip ( "master", 0 );
+       groups["master"] = group;
+       strips[0] = dynamic_cast<Strip*>( group );
+       
+       group = new Strip ( "strip_1", 0 );
+       groups["strip_1"] = group;
+       strips[0] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "cursor" );
+       groups["cursor"] = group;
+       
+       group = new Strip ( "strip_2", 1 );
+       groups["strip_2"] = group;
+       strips[1] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "automation" );
+       groups["automation"] = group;
+       
+       group = new Group ( "functions" );
+       groups["functions"] = group;
+       
+       group = new Strip ( "strip_3", 2 );
+       groups["strip_3"] = group;
+       strips[2] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "display" );
+       groups["display"] = group;
+       
+       group = new Strip ( "strip_4", 3 );
+       groups["strip_4"] = group;
+       strips[3] = dynamic_cast<Strip*>( group );
+       
+       group = new Strip ( "strip_5", 4 );
+       groups["strip_5"] = group;
+       strips[4] = dynamic_cast<Strip*>( group );
+       
+       group = new Strip ( "strip_6", 5 );
+       groups["strip_6"] = group;
+       strips[5] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "transport" );
+       groups["transport"] = group;
+       
+       group = new Strip ( "strip_7", 6 );
+       groups["strip_7"] = group;
+       strips[6] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "modifiers" );
+       groups["modifiers"] = group;
+       
+       group = new Group ( "bank" );
+       groups["bank"] = group;
+       
+
+       // initialise controls
+       Fader * fader = 0;
+       Pot * pot = 0;
+       Button * button = 0;
+       Led * led = 0;
+
+       group = groups["strip_1"];
+       fader = new Fader ( 0, 1, "gain", *group );
+       faders[0x00] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_2"];
+       fader = new Fader ( 1, 2, "gain", *group );
+       faders[0x01] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_3"];
+       fader = new Fader ( 2, 3, "gain", *group );
+       faders[0x02] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_4"];
+       fader = new Fader ( 3, 4, "gain", *group );
+       faders[0x03] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_5"];
+       fader = new Fader ( 4, 5, "gain", *group );
+       faders[0x04] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_6"];
+       fader = new Fader ( 5, 6, "gain", *group );
+       faders[0x05] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_7"];
+       fader = new Fader ( 6, 7, "gain", *group );
+       faders[0x06] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["master"];
+       fader = new Fader ( 7, 1, "gain", *group );
+       faders[0x07] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_1"];
+       pot = new Pot ( 16, 1, "vpot", *group );
+       pots[0x10] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_2"];
+       pot = new Pot ( 17, 2, "vpot", *group );
+       pots[0x11] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_3"];
+       pot = new Pot ( 18, 3, "vpot", *group );
+       pots[0x12] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_4"];
+       pot = new Pot ( 19, 4, "vpot", *group );
+       pots[0x13] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_5"];
+       pot = new Pot ( 20, 5, "vpot", *group );
+       pots[0x14] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_6"];
+       pot = new Pot ( 21, 6, "vpot", *group );
+       pots[0x15] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_7"];
+       pot = new Pot ( 22, 7, "vpot", *group );
+       pots[0x16] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["none"];
+       pot = new Jog ( 23, 1, "jog", *group );
+       pots[0x17] = pot;
+       controls.push_back( pot );
+       controls_by_name["jog"] = pot;
+       group->add( *pot );
+
+       group = groups["none"];
+       pot = new Pot ( 46, 1, "external", *group );
+       pots[0x2e] = pot;
+       controls.push_back( pot );
+       controls_by_name["external"] = pot;
+       group->add( *pot );
+
+       group = groups["strip_1"];
+       button = new Button ( 24, 1, "recenable", *group );
+       buttons[0x18] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 25, 2, "recenable", *group );
+       buttons[0x19] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 26, 3, "recenable", *group );
+       buttons[0x1a] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 27, 4, "recenable", *group );
+       buttons[0x1b] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 28, 5, "recenable", *group );
+       buttons[0x1c] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 29, 6, "recenable", *group );
+       buttons[0x1d] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 30, 7, "recenable", *group );
+       buttons[0x1e] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 32, 1, "solo", *group );
+       buttons[0x20] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 33, 2, "solo", *group );
+       buttons[0x21] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 34, 3, "solo", *group );
+       buttons[0x22] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 35, 4, "solo", *group );
+       buttons[0x23] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 36, 5, "solo", *group );
+       buttons[0x24] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 37, 6, "solo", *group );
+       buttons[0x25] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 38, 7, "solo", *group );
+       buttons[0x26] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 16, 1, "mute", *group );
+       buttons[0x10] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 17, 2, "mute", *group );
+       buttons[0x11] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 18, 3, "mute", *group );
+       buttons[0x12] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 19, 4, "mute", *group );
+       buttons[0x13] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 20, 5, "mute", *group );
+       buttons[0x14] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 21, 6, "mute", *group );
+       buttons[0x15] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 22, 7, "mute", *group );
+       buttons[0x16] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 0, 1, "select", *group );
+       buttons[0x00] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 1, 2, "select", *group );
+       buttons[0x01] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 2, 3, "select", *group );
+       buttons[0x02] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 3, 4, "select", *group );
+       buttons[0x03] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 4, 5, "select", *group );
+       buttons[0x04] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 5, 6, "select", *group );
+       buttons[0x05] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 6, 7, "select", *group );
+       buttons[0x06] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 8, 1, "vselect", *group );
+       buttons[0x08] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 9, 2, "vselect", *group );
+       buttons[0x09] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 10, 3, "vselect", *group );
+       buttons[0x0a] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 11, 4, "vselect", *group );
+       buttons[0x0b] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 12, 5, "vselect", *group );
+       buttons[0x0c] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 13, 6, "vselect", *group );
+       buttons[0x0d] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 14, 7, "vselect", *group );
+       buttons[0x0e] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 40, 1, "io", *group );
+       buttons[0x28] = button;
+       controls.push_back( button );
+       controls_by_name["io"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 90, 1, "sends", *group );
+       buttons[0x5a] = button;
+       controls.push_back( button );
+       controls_by_name["sends"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 89, 1, "pan", *group );
+       buttons[0x59] = button;
+       controls.push_back( button );
+       controls_by_name["pan"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 87, 1, "plugin", *group );
+       buttons[0x57] = button;
+       controls.push_back( button );
+       controls_by_name["plugin"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 88, 1, "drop", *group );
+       buttons[0x58] = button;
+       controls.push_back( button );
+       controls_by_name["drop"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 45, 1, "zoom", *group );
+       buttons[0x2d] = button;
+       controls.push_back( button );
+       controls_by_name["zoom"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 46, 1, "left", *group );
+       buttons[0x2e] = button;
+       controls.push_back( button );
+       controls_by_name["left"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 47, 1, "right", *group );
+       buttons[0x2f] = button;
+       controls.push_back( button );
+       controls_by_name["right"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 48, 1, "channel_left", *group );
+       buttons[0x30] = button;
+       controls.push_back( button );
+       controls_by_name["channel_left"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 49, 1, "channel_right", *group );
+       buttons[0x31] = button;
+       controls.push_back( button );
+       controls_by_name["channel_right"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 50, 1, "scrub", *group );
+       buttons[0x32] = button;
+       controls.push_back( button );
+       controls_by_name["scrub"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 86, 1, "edit", *group );
+       buttons[0x56] = button;
+       controls.push_back( button );
+       controls_by_name["edit"] = button;
+       group->add( *button );
+
+       group = groups["display"];
+       button = new Button ( 52, 1, "name_value", *group );
+       buttons[0x34] = button;
+       controls.push_back( button );
+       controls_by_name["name_value"] = button;
+       group->add( *button );
+
+       group = groups["display"];
+       button = new Button ( 53, 1, "smpte_beats", *group );
+       buttons[0x35] = button;
+       controls.push_back( button );
+       controls_by_name["smpte_beats"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 54, 1, "F1", *group );
+       buttons[0x36] = button;
+       controls.push_back( button );
+       controls_by_name["F1"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 55, 1, "F2", *group );
+       buttons[0x37] = button;
+       controls.push_back( button );
+       controls_by_name["F2"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 56, 1, "F3", *group );
+       buttons[0x38] = button;
+       controls.push_back( button );
+       controls_by_name["F3"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 57, 1, "F4", *group );
+       buttons[0x39] = button;
+       controls.push_back( button );
+       controls_by_name["F4"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 58, 1, "F5", *group );
+       buttons[0x3a] = button;
+       controls.push_back( button );
+       controls_by_name["F5"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 59, 1, "F6", *group );
+       buttons[0x3b] = button;
+       controls.push_back( button );
+       controls_by_name["F6"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 60, 1, "F7", *group );
+       buttons[0x3c] = button;
+       controls.push_back( button );
+       controls_by_name["F7"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 61, 1, "F8", *group );
+       buttons[0x3d] = button;
+       controls.push_back( button );
+       controls_by_name["F8"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 62, 1, "F9", *group );
+       buttons[0x3e] = button;
+       controls.push_back( button );
+       controls_by_name["F9"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 63, 1, "F10", *group );
+       buttons[0x3f] = button;
+       controls.push_back( button );
+       controls_by_name["F10"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 64, 1, "F11", *group );
+       buttons[0x40] = button;
+       controls.push_back( button );
+       controls_by_name["F11"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 65, 1, "F12", *group );
+       buttons[0x41] = button;
+       controls.push_back( button );
+       controls_by_name["F12"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 66, 1, "F13", *group );
+       buttons[0x42] = button;
+       controls.push_back( button );
+       controls_by_name["F13"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 67, 1, "F14", *group );
+       buttons[0x43] = button;
+       controls.push_back( button );
+       controls_by_name["F14"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 68, 1, "F15", *group );
+       buttons[0x44] = button;
+       controls.push_back( button );
+       controls_by_name["F15"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 69, 1, "F16", *group );
+       buttons[0x45] = button;
+       controls.push_back( button );
+       controls_by_name["F16"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 39, 1, "global_solo", *group );
+       buttons[0x27] = button;
+       controls.push_back( button );
+       controls_by_name["global_solo"] = button;
+       group->add( *button );
+
+       group = groups["modifiers"];
+       button = new Button ( 80, 1, "option", *group );
+       buttons[0x50] = button;
+       controls.push_back( button );
+       controls_by_name["option"] = button;
+       group->add( *button );
+
+       group = groups["modifiers"];
+       button = new Button ( 73, 1, "cmd_alt", *group );
+       buttons[0x49] = button;
+       controls.push_back( button );
+       controls_by_name["cmd_alt"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 74, 1, "on", *group );
+       buttons[0x4a] = button;
+       controls.push_back( button );
+       controls_by_name["on"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 75, 1, "rec_ready", *group );
+       buttons[0x4b] = button;
+       controls.push_back( button );
+       controls_by_name["rec_ready"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 76, 1, "undo", *group );
+       buttons[0x4c] = button;
+       controls.push_back( button );
+       controls_by_name["undo"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 95, 1, "snapshot", *group );
+       buttons[0x5f] = button;
+       controls.push_back( button );
+       controls_by_name["snapshot"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 79, 1, "redo", *group );
+       buttons[0x4f] = button;
+       controls.push_back( button );
+       controls_by_name["redo"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 71, 1, "marker", *group );
+       buttons[0x47] = button;
+       controls.push_back( button );
+       controls_by_name["marker"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 81, 1, "enter", *group );
+       buttons[0x51] = button;
+       controls.push_back( button );
+       controls_by_name["enter"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 82, 1, "cancel", *group );
+       buttons[0x52] = button;
+       controls.push_back( button );
+       controls_by_name["cancel"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 83, 1, "mixer", *group );
+       buttons[0x53] = button;
+       controls.push_back( button );
+       controls_by_name["mixer"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 77, 1, "save", *group );
+       buttons[0x4d] = button;
+       controls.push_back( button );
+       controls_by_name["save"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 91, 1, "frm_left", *group );
+       buttons[0x5b] = button;
+       controls.push_back( button );
+       controls_by_name["frm_left"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 92, 1, "frm_right", *group );
+       buttons[0x5c] = button;
+       controls.push_back( button );
+       controls_by_name["frm_right"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 70, 1, "loop", *group );
+       buttons[0x46] = button;
+       controls.push_back( button );
+       controls_by_name["loop"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 72, 1, "punch_in", *group );
+       buttons[0x48] = button;
+       controls.push_back( button );
+       controls_by_name["punch_in"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 78, 1, "punch_out", *group );
+       buttons[0x4e] = button;
+       controls.push_back( button );
+       controls_by_name["punch_out"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 42, 1, "home", *group );
+       buttons[0x2a] = button;
+       controls.push_back( button );
+       controls_by_name["home"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 41, 1, "end", *group );
+       buttons[0x29] = button;
+       controls.push_back( button );
+       controls_by_name["end"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 44, 1, "rewind", *group );
+       buttons[0x2c] = button;
+       controls.push_back( button );
+       controls_by_name["rewind"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 43, 1, "ffwd", *group );
+       buttons[0x2b] = button;
+       controls.push_back( button );
+       controls_by_name["ffwd"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 93, 1, "stop", *group );
+       buttons[0x5d] = button;
+       controls.push_back( button );
+       controls_by_name["stop"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 94, 1, "play", *group );
+       buttons[0x5e] = button;
+       controls.push_back( button );
+       controls_by_name["play"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 31, 1, "record", *group );
+       buttons[0x1f] = button;
+       controls.push_back( button );
+       controls_by_name["record"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 96, 1, "cursor_up", *group );
+       buttons[0x60] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_up"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 97, 1, "cursor_down", *group );
+       buttons[0x61] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_down"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 98, 1, "cursor_left", *group );
+       buttons[0x62] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_left"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 99, 1, "cursor_right", *group );
+       buttons[0x63] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_right"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 100, 1, "dyn", *group );
+       buttons[0x64] = button;
+       controls.push_back( button );
+       controls_by_name["dyn"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 101, 1, "flip", *group );
+       buttons[0x65] = button;
+       controls.push_back( button );
+       controls_by_name["flip"] = button;
+       group->add( *button );
+
+       group = groups["user"];
+       button = new Button ( 102, 1, "user_a", *group );
+       buttons[0x66] = button;
+       controls.push_back( button );
+       controls_by_name["user_a"] = button;
+       group->add( *button );
+
+       group = groups["user"];
+       button = new Button ( 103, 1, "user_b", *group );
+       buttons[0x67] = button;
+       controls.push_back( button );
+       controls_by_name["user_b"] = button;
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 104, 1, "fader_touch", *group );
+       buttons[0x68] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 105, 2, "fader_touch", *group );
+       buttons[0x69] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 106, 3, "fader_touch", *group );
+       buttons[0x6a] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 107, 4, "fader_touch", *group );
+       buttons[0x6b] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 108, 5, "fader_touch", *group );
+       buttons[0x6c] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 109, 6, "fader_touch", *group );
+       buttons[0x6d] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 110, 7, "fader_touch", *group );
+       buttons[0x6e] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["master"];
+       button = new Button ( 111, 1, "fader_touch", *group );
+       buttons[0x6f] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["master"];
+       button = new Button ( 23, 1, "mute", *group );
+       buttons[0x17] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 51, 1, "clicking", *group );
+       buttons[0x33] = button;
+       controls.push_back( button );
+       controls_by_name["clicking"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       led = new Led ( 113, 1, "smpte", *group );
+       leds[0x71] = led;
+       controls.push_back( led );
+       controls_by_name["smpte"] = led;
+       group->add( *led );
+
+       group = groups["none"];
+       led = new Led ( 114, 1, "beats", *group );
+       leds[0x72] = led;
+       controls.push_back( led );
+       controls_by_name["beats"] = led;
+       group->add( *led );
+
+       group = groups["none"];
+       led = new Led ( 115, 1, "solo", *group );
+       leds[0x73] = led;
+       controls.push_back( led );
+       controls_by_name["solo"] = led;
+       group->add( *led );
+
+       group = groups["none"];
+       led = new Led ( 118, 1, "relay_click", *group );
+       leds[0x76] = led;
+       controls.push_back( led );
+       controls_by_name["relay_click"] = led;
+       group->add( *led );
+
+}
+
+void Mackie::BcfSurface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
+{
+       if ( bs != press && bs != release )
+       {
+               mbh.update_led( button, none );
+               return;
+       }
+       
+       LedState ls;
+       switch ( button.id() )
+       {
+
+               case 0x9028: // io
+                       switch ( bs ) {
+                               case press: ls = mbh.io_press( button ); break;
+                               case release: ls = mbh.io_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905a: // sends
+                       switch ( bs ) {
+                               case press: ls = mbh.sends_press( button ); break;
+                               case release: ls = mbh.sends_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9059: // pan
+                       switch ( bs ) {
+                               case press: ls = mbh.pan_press( button ); break;
+                               case release: ls = mbh.pan_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9057: // plugin
+                       switch ( bs ) {
+                               case press: ls = mbh.plugin_press( button ); break;
+                               case release: ls = mbh.plugin_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9058: // drop
+                       switch ( bs ) {
+                               case press: ls = mbh.drop_press( button ); break;
+                               case release: ls = mbh.drop_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902d: // zoom
+                       switch ( bs ) {
+                               case press: ls = mbh.zoom_press( button ); break;
+                               case release: ls = mbh.zoom_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902e: // left
+                       switch ( bs ) {
+                               case press: ls = mbh.left_press( button ); break;
+                               case release: ls = mbh.left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902f: // right
+                       switch ( bs ) {
+                               case press: ls = mbh.right_press( button ); break;
+                               case release: ls = mbh.right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9030: // channel_left
+                       switch ( bs ) {
+                               case press: ls = mbh.channel_left_press( button ); break;
+                               case release: ls = mbh.channel_left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9031: // channel_right
+                       switch ( bs ) {
+                               case press: ls = mbh.channel_right_press( button ); break;
+                               case release: ls = mbh.channel_right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9032: // scrub
+                       switch ( bs ) {
+                               case press: ls = mbh.scrub_press( button ); break;
+                               case release: ls = mbh.scrub_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9056: // edit
+                       switch ( bs ) {
+                               case press: ls = mbh.edit_press( button ); break;
+                               case release: ls = mbh.edit_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9034: // name_value
+                       switch ( bs ) {
+                               case press: ls = mbh.name_value_press( button ); break;
+                               case release: ls = mbh.name_value_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9035: // smpte_beats
+                       switch ( bs ) {
+                               case press: ls = mbh.smpte_beats_press( button ); break;
+                               case release: ls = mbh.smpte_beats_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9036: // F1
+                       switch ( bs ) {
+                               case press: ls = mbh.F1_press( button ); break;
+                               case release: ls = mbh.F1_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9037: // F2
+                       switch ( bs ) {
+                               case press: ls = mbh.F2_press( button ); break;
+                               case release: ls = mbh.F2_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9038: // F3
+                       switch ( bs ) {
+                               case press: ls = mbh.F3_press( button ); break;
+                               case release: ls = mbh.F3_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9039: // F4
+                       switch ( bs ) {
+                               case press: ls = mbh.F4_press( button ); break;
+                               case release: ls = mbh.F4_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903a: // F5
+                       switch ( bs ) {
+                               case press: ls = mbh.F5_press( button ); break;
+                               case release: ls = mbh.F5_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903b: // F6
+                       switch ( bs ) {
+                               case press: ls = mbh.F6_press( button ); break;
+                               case release: ls = mbh.F6_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903c: // F7
+                       switch ( bs ) {
+                               case press: ls = mbh.F7_press( button ); break;
+                               case release: ls = mbh.F7_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903d: // F8
+                       switch ( bs ) {
+                               case press: ls = mbh.F8_press( button ); break;
+                               case release: ls = mbh.F8_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903e: // F9
+                       switch ( bs ) {
+                               case press: ls = mbh.F9_press( button ); break;
+                               case release: ls = mbh.F9_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903f: // F10
+                       switch ( bs ) {
+                               case press: ls = mbh.F10_press( button ); break;
+                               case release: ls = mbh.F10_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9040: // F11
+                       switch ( bs ) {
+                               case press: ls = mbh.F11_press( button ); break;
+                               case release: ls = mbh.F11_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9041: // F12
+                       switch ( bs ) {
+                               case press: ls = mbh.F12_press( button ); break;
+                               case release: ls = mbh.F12_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9042: // F13
+                       switch ( bs ) {
+                               case press: ls = mbh.F13_press( button ); break;
+                               case release: ls = mbh.F13_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9043: // F14
+                       switch ( bs ) {
+                               case press: ls = mbh.F14_press( button ); break;
+                               case release: ls = mbh.F14_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9044: // F15
+                       switch ( bs ) {
+                               case press: ls = mbh.F15_press( button ); break;
+                               case release: ls = mbh.F15_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9045: // F16
+                       switch ( bs ) {
+                               case press: ls = mbh.F16_press( button ); break;
+                               case release: ls = mbh.F16_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9027: // global_solo
+                       switch ( bs ) {
+                               case press: ls = mbh.global_solo_press( button ); break;
+                               case release: ls = mbh.global_solo_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9050: // option
+                       switch ( bs ) {
+                               case press: ls = mbh.option_press( button ); break;
+                               case release: ls = mbh.option_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9049: // cmd_alt
+                       switch ( bs ) {
+                               case press: ls = mbh.cmd_alt_press( button ); break;
+                               case release: ls = mbh.cmd_alt_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904a: // on
+                       switch ( bs ) {
+                               case press: ls = mbh.on_press( button ); break;
+                               case release: ls = mbh.on_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904b: // rec_ready
+                       switch ( bs ) {
+                               case press: ls = mbh.rec_ready_press( button ); break;
+                               case release: ls = mbh.rec_ready_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904c: // undo
+                       switch ( bs ) {
+                               case press: ls = mbh.undo_press( button ); break;
+                               case release: ls = mbh.undo_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905f: // snapshot
+                       switch ( bs ) {
+                               case press: ls = mbh.snapshot_press( button ); break;
+                               case release: ls = mbh.snapshot_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904f: // redo
+                       switch ( bs ) {
+                               case press: ls = mbh.redo_press( button ); break;
+                               case release: ls = mbh.redo_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9047: // marker
+                       switch ( bs ) {
+                               case press: ls = mbh.marker_press( button ); break;
+                               case release: ls = mbh.marker_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9051: // enter
+                       switch ( bs ) {
+                               case press: ls = mbh.enter_press( button ); break;
+                               case release: ls = mbh.enter_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9052: // cancel
+                       switch ( bs ) {
+                               case press: ls = mbh.cancel_press( button ); break;
+                               case release: ls = mbh.cancel_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9053: // mixer
+                       switch ( bs ) {
+                               case press: ls = mbh.mixer_press( button ); break;
+                               case release: ls = mbh.mixer_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904d: // save
+                       switch ( bs ) {
+                               case press: ls = mbh.save_press( button ); break;
+                               case release: ls = mbh.save_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905b: // frm_left
+                       switch ( bs ) {
+                               case press: ls = mbh.frm_left_press( button ); break;
+                               case release: ls = mbh.frm_left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905c: // frm_right
+                       switch ( bs ) {
+                               case press: ls = mbh.frm_right_press( button ); break;
+                               case release: ls = mbh.frm_right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9046: // loop
+                       switch ( bs ) {
+                               case press: ls = mbh.loop_press( button ); break;
+                               case release: ls = mbh.loop_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9048: // punch_in
+                       switch ( bs ) {
+                               case press: ls = mbh.punch_in_press( button ); break;
+                               case release: ls = mbh.punch_in_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904e: // punch_out
+                       switch ( bs ) {
+                               case press: ls = mbh.punch_out_press( button ); break;
+                               case release: ls = mbh.punch_out_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902a: // home
+                       switch ( bs ) {
+                               case press: ls = mbh.home_press( button ); break;
+                               case release: ls = mbh.home_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9029: // end
+                       switch ( bs ) {
+                               case press: ls = mbh.end_press( button ); break;
+                               case release: ls = mbh.end_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902c: // rewind
+                       switch ( bs ) {
+                               case press: ls = mbh.rewind_press( button ); break;
+                               case release: ls = mbh.rewind_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902b: // ffwd
+                       switch ( bs ) {
+                               case press: ls = mbh.ffwd_press( button ); break;
+                               case release: ls = mbh.ffwd_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905d: // stop
+                       switch ( bs ) {
+                               case press: ls = mbh.stop_press( button ); break;
+                               case release: ls = mbh.stop_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905e: // play
+                       switch ( bs ) {
+                               case press: ls = mbh.play_press( button ); break;
+                               case release: ls = mbh.play_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x901f: // record
+                       switch ( bs ) {
+                               case press: ls = mbh.record_press( button ); break;
+                               case release: ls = mbh.record_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9060: // cursor_up
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_up_press( button ); break;
+                               case release: ls = mbh.cursor_up_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9061: // cursor_down
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_down_press( button ); break;
+                               case release: ls = mbh.cursor_down_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9062: // cursor_left
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_left_press( button ); break;
+                               case release: ls = mbh.cursor_left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9063: // cursor_right
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_right_press( button ); break;
+                               case release: ls = mbh.cursor_right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9064: // dyn
+                       switch ( bs ) {
+                               case press: ls = mbh.dyn_press( button ); break;
+                               case release: ls = mbh.dyn_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9065: // flip
+                       switch ( bs ) {
+                               case press: ls = mbh.flip_press( button ); break;
+                               case release: ls = mbh.flip_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9066: // user_a
+                       switch ( bs ) {
+                               case press: ls = mbh.user_a_press( button ); break;
+                               case release: ls = mbh.user_a_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9067: // user_b
+                       switch ( bs ) {
+                               case press: ls = mbh.user_b_press( button ); break;
+                               case release: ls = mbh.user_b_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9033: // clicking
+                       switch ( bs ) {
+                               case press: ls = mbh.clicking_press( button ); break;
+                               case release: ls = mbh.clicking_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+       }
+       mbh.update_led( button, ls );
+}
index e9808119b2752a9eb0124812475a6c1eb5212984..d73530f95b773d2dcd3ce076e16f955caaa6eb0f 100644 (file)
@@ -45,6 +45,76 @@ Strip::Strip( const std::string & name, int index )
 {
 }
 
+/**
+       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() );
+       }
+}
+
+Control::Control( int id, int ordinal, std::string name, Group & group )
+: _id( id )
+, _ordinal( ordinal )
+, _name( name )
+, _group( group )
+, _in_use( false )
+, _in_use_timeout( 250 )
+{
+}
+
 /**
  generated with
 
@@ -107,3 +177,58 @@ Button & Strip::fader_touch()
                throw MackieControlException( "fader_touch is null" );
        return *_fader_touch;
 }
+
+bool Control::in_use() const
+{
+       return _in_use;
+}
+
+Control & Control::in_use( bool rhs )
+{
+       _in_use = rhs;
+       return *this;
+}
+
+ostream & Mackie::operator << ( ostream & os, const Mackie::Control & control )
+{
+       os << typeid( control ).name();
+       os << " { ";
+       os << "name: " << control.name();
+       os << ", ";
+       os << "id: " << "0x" << setw(4) << setfill('0') << hex << control.id() << setfill(' ');
+       os << ", ";
+       os << "type: " << "0x" << setw(2) << setfill('0') << hex << control.type() << setfill(' ');
+       os << ", ";
+       os << "raw_id: " << "0x" << setw(2) << setfill('0') << hex << control.raw_id() << setfill(' ');
+       os << ", ";
+       os << "ordinal: " << dec << control.ordinal();
+       os << ", ";
+       os << "group: " << control.group().name();
+       os << " }";
+       
+       return os;
+}
+
+std::ostream & Mackie::operator << ( std::ostream & os, const Strip & strip )
+{
+       os << typeid( strip ).name();
+       os << " { ";
+       os << "has_solo: " << boolalpha << strip.has_solo();
+       os << ", ";
+       os << "has_recenable: " << boolalpha << strip.has_recenable();
+       os << ", ";
+       os << "has_mute: " << boolalpha << strip.has_mute();
+       os << ", ";
+       os << "has_select: " << boolalpha << strip.has_select();
+       os << ", ";
+       os << "has_vselect: " << boolalpha << strip.has_vselect();
+       os << ", ";
+       os << "has_fader_touch: " << boolalpha << strip.has_fader_touch();
+       os << ", ";
+       os << "has_vpot: " << boolalpha << strip.has_vpot();
+       os << ", ";
+       os << "has_gain: " << boolalpha << strip.has_gain();
+       os << " }";
+       
+       return os;
+}
index 1092c40453de6e7aba90264609cdf470aefc4a68..84283f301697ea1299db941ebe7331de14ceb1e9 100644 (file)
@@ -18,6 +18,8 @@
 #ifndef mackie_controls_h
 #define mackie_controls_h
 
+#include <sigc++/sigc++.h>
+
 #include <map>
 #include <vector>
 #include <string>
@@ -83,6 +85,9 @@ class Fader;
 class Strip : public Group
 {
 public:
+       /**
+               \param is the index of the strip. 0-based.
+       */
        Strip( const std::string & name, int index );
        
        virtual bool is_strip() const
@@ -92,10 +97,11 @@ public:
        
        virtual void add( Control & control );
        
-       /// This is the index of the strip
+       /// This is the index of the strip. zero-based.
        int index() const { return _index; }
        
        /// This is for Surface only
+       /// index is zero-based
        void index( int rhs ) { _index = rhs; }
        
        Button & solo();
@@ -107,14 +113,14 @@ public:
        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; }
+       bool has_solo() const { return _solo != 0; }
+       bool has_recenable() const { return _recenable != 0; }
+       bool has_mute() const { return _mute != 0; }
+       bool has_select() const { return _select != 0; }
+       bool has_vselect() const { return _vselect != 0; }
+       bool has_fader_touch() const { return _fader_touch != 0; }
+       bool has_vpot() const { return _vpot != 0; }
+       bool has_gain() const { return _gain != 0; }
        
 private:
        Button * _solo;
@@ -128,6 +134,8 @@ private:
        int _index;
 };
 
+std::ostream & operator << ( std::ostream &, const Strip & );
+
 class MasterStrip : public Strip
 {
 public:
@@ -151,13 +159,9 @@ class Led;
 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 )
-       {
-       }
+       enum type_t { type_led, type_led_ring, type_fader = 0xe0, type_button = 0x90, type_pot = 0xb0 };
        
+       Control( int id, int ordinal, std::string name, Group & group );
        virtual ~Control() {}
        
        virtual const Led & led() const
@@ -165,17 +169,20 @@ public:
                throw MackieControlException( "no led available" );
        }
 
-       /// The midi id of the control
+       /// type() << 8 + midi id of the control. This
+       /// provides a unique id for any control on the surface.
        int id() const
        {
-               return _id;
+               return ( type() << 8 ) + _id;
        }
        
+       /// the value of the second bytes of the message. It's
+       /// the id of the control, but only guaranteed to be
+       /// unique within the control type.
+       int raw_id() const { return _id; }
+       
        /// The 1-based number of the control
-       int ordinal() const
-       {
-               return _ordinal;
-       }
+       int ordinal() const { return _ordinal; }
        
        const std::string & name() const
        {
@@ -204,11 +211,32 @@ public:
        
        virtual type_t type() const = 0;
        
+       /// Return true if this control is the one and only Jog Wheel
+       virtual bool is_jog() const { return false; }
+
+       /**
+               Return true if the controlis in use, or false otherwise. For buttons
+               this returns true if the button is currently being held down. For
+               faders, the touch button has not been released. For pots, this returns
+               true from the first move event until a timeout after the last move event.
+       */
+       virtual bool in_use() const;
+       virtual Control & in_use( bool );
+       
+       /// The timeout value for this control. Normally defaulted to 250ms, but
+       /// certain controls (ie jog wheel) may want to override it.
+       virtual unsigned int in_use_timeout() { return _in_use_timeout; }
+
+       /// Keep track of the timeout so it can be updated with more incoming events
+       sigc::connection in_use_connection;
+       
 private:
        int _id;
        int _ordinal;
        std::string _name;
        Group & _group;
+       bool _in_use;
+       unsigned int _in_use_timeout;
 };
 
 std::ostream & operator << ( std::ostream & os, const Control & control );
@@ -218,18 +246,10 @@ 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
@@ -260,7 +280,7 @@ public:
        }
        
        virtual type_t type() const { return type_button; };
-
+       
 private:
        Led _led;
 };
@@ -296,6 +316,17 @@ private:
        LedRing _led_ring;
 };
 
+class Jog : public Pot
+{
+public:
+       Jog( int id, int ordinal, std::string name, Group & group )
+       : Pot( id, ordinal, name, group )
+       {
+       }
+
+       virtual bool is_jog() const { return true; }
+};
+
 }
 
 #endif
diff --git a/libs/surfaces/mackie/dummy_port.cc b/libs/surfaces/mackie/dummy_port.cc
new file mode 100644 (file)
index 0000000..7654f8f
--- /dev/null
@@ -0,0 +1,58 @@
+#include "dummy_port.h"
+
+#include "midi_byte_array.h"
+
+#include <midi++/port.h>
+#include <midi++/types.h>
+
+#include <iostream>
+
+using namespace Mackie;
+using namespace std;
+
+DummyPort::DummyPort()
+{
+}
+
+DummyPort::~DummyPort()
+{
+}
+
+       
+void DummyPort::open()
+{
+       cout << "DummyPort::open" << endl;
+}
+
+       
+void DummyPort::close()
+{
+       cout << "DummyPort::close" << endl;
+}
+
+
+MidiByteArray DummyPort::read()
+{
+       cout << "DummyPort::read" << endl;
+       return MidiByteArray();
+}
+
+       
+void DummyPort::write( const MidiByteArray & mba )
+{
+       cout << "DummyPort::write " << mba << endl;
+}
+
+MidiByteArray empty_midi_byte_array;
+
+const MidiByteArray & DummyPort::sysex_hdr() const
+{
+       cout << "DummyPort::sysex_hdr" << endl;
+       return empty_midi_byte_array;
+}
+
+int DummyPort::strips() const
+{
+       cout << "DummyPort::strips" << endl;
+       return 0;
+}
diff --git a/libs/surfaces/mackie/dummy_port.h b/libs/surfaces/mackie/dummy_port.h
new file mode 100644 (file)
index 0000000..4ed0a30
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+       Copyright (C) 2008 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 dummy_port_h
+#define dummy_port_h
+
+#include "surface_port.h"
+
+#include "midi_byte_array.h"
+
+namespace MIDI {
+       class Port;
+}
+
+namespace Mackie
+{
+
+/**
+       A Dummy Port, to catch things that shouldn't be sent.
+*/
+class DummyPort : public SurfacePort
+{
+public:
+       DummyPort();
+       virtual ~DummyPort();
+       
+       // when this is successful, active() should return true
+       virtual void open();
+       
+       // subclasses should call this before doing their own close
+       virtual void close();
+
+       /// read bytes from the port. They'll either end up in the
+       /// parser, or if that's not active they'll be returned
+       virtual MidiByteArray read();
+       
+       /// an easier way to output bytes via midi
+       virtual void write( const MidiByteArray & );
+
+       virtual const MidiByteArray & sysex_hdr() const;
+       virtual int strips() const;
+};     
+
+}
+
+#endif
index eda485b5d65a4a90b395f63df3d088fa29141872..7872d47a78667ebc2c75d3dbae7ec3c05f82bbeb 100644 (file)
@@ -64,9 +64,22 @@ new_mackie_protocol (ControlProtocolDescriptor* descriptor, Session* s)
 void
 delete_mackie_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
 {
-       delete cp;
+       try
+       {
+               delete cp;
+       }
+       catch ( exception & e )
+       {
+               cout << "Exception caught trying to destroy MackieControlProtocol: " << e.what() << endl;
+       }
 }
 
+/**
+       This is called on startup to check whether the lib should be loaded.
+
+       So anything that can be changed in the UI should not be used here to
+       prevent loading of the lib.
+*/
 bool
 probe_mackie_protocol (ControlProtocolDescriptor* descriptor)
 {
@@ -79,7 +92,11 @@ static ControlProtocolDescriptor mackie_descriptor = {
        ptr : 0,
        module : 0,
        mandatory : 0,
-       supports_feedback : true,
+       // actually, the surface does support feedback, but all this
+       // flag does is show a submenu on the UI, which is useless for the mackie
+       // because feedback is always on. In any case, who'd want to use the
+       // mcu without the motorised sliders doing their thing?
+       supports_feedback : false,
        probe : probe_mackie_protocol,
        initialize : new_mackie_protocol,
        destroy : delete_mackie_protocol
index f7ac2ab6d5156e4e3d1846332bc96295efc6b95e..2db07beabda63d11b1d67cfef877989e730987e4 100644 (file)
@@ -689,3 +689,23 @@ LedState MackieButtonHandler::global_solo_release( Button & button )
 {
        return default_button_press( button );
 }
+
+LedState MackieButtonHandler::drop_press( Button & button )
+{
+       return default_button_press( button );
+}
+
+LedState MackieButtonHandler::drop_release( Button & button )
+{
+       return default_button_press( button );
+}
+
+LedState MackieButtonHandler::save_press( Button & button )
+{
+       return default_button_press( button );
+}
+
+LedState MackieButtonHandler::save_release( Button & button )
+{
+       return default_button_press( button );
+}
index ee4187c7ce261857d2a9910a48082c7d8fc6c600..2e8bc649be357abb38a0d8a6d21ae64546791379 100644 (file)
@@ -220,6 +220,12 @@ public:
 
        virtual LedState global_solo_press( Button & );
        virtual LedState global_solo_release( Button & );
+
+       virtual LedState drop_press( Button & );
+       virtual LedState drop_release( Button & );
+
+       virtual LedState save_press( Button & );
+       virtual LedState save_release( Button & );
 };
 
 }
index f7886e078f7579c5a9d522264e76cb2b559d855f..b29c26b0d37850d4bdd09d576f8e922dd5b0bb08 100644 (file)
@@ -14,7 +14,6 @@
        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>
@@ -22,6 +21,7 @@
 #include <cmath>
 #include <sstream>
 #include <vector>
+#include <iomanip>
 
 #define __STDC_FORMAT_MACROS
 #include <inttypes.h>
 #include <pbd/pthread_utils.h>
 #include <pbd/error.h>
 #include <pbd/memento_command.h>
+#include <pbd/convert.h>
 
 #include <ardour/route.h>
 #include <ardour/session.h>
 #include <ardour/location.h>
 #include <ardour/dB.h>
 #include <ardour/panner.h>
+#include <ardour/tempo.h>
+#include <ardour/types.h>
 
 #include "mackie_control_protocol.h"
 
@@ -68,27 +71,6 @@ using boost::shared_ptr;
 
 MackieMidiBuilder builder;
 
-// Copied from tranzport_control_protocol.cc
-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);
-}
-
-/*
-       Copied from tranzport_control_protocol.cc
-       TODO this seems to return slightly wrong values, namely
-       with the UI slider at max, we get a 0.99something value.
-*/
-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);
-}
-
 MackieControlProtocol::MackieControlProtocol (Session& session)
        : ControlProtocol  (session, X_("Mackie"))
        , _current_initial_bank( 0 )
@@ -98,15 +80,21 @@ MackieControlProtocol::MackieControlProtocol (Session& session)
        , _polling( true )
        , pfd( 0 )
        , nfds( 0 )
+       , _jog_wheel( *this )
+       , _timecode_type( ARDOUR::AnyTime::BBT )
 {
-       //cout << "MackieControlProtocol::MackieControlProtocol" << endl;
+#ifdef DEBUG
+       cout << "MackieControlProtocol::MackieControlProtocol" << endl;
+#endif
        // will start reading from ports, as soon as there are some
        pthread_create_and_store (X_("mackie monitor"), &thread, 0, _monitor_work, this);
 }
 
 MackieControlProtocol::~MackieControlProtocol()
 {
-       //cout << "~MackieControlProtocol::MackieControlProtocol" << endl;
+#ifdef DEBUG
+       cout << "~MackieControlProtocol::MackieControlProtocol" << endl;
+#endif
        try
        {
                close();
@@ -119,7 +107,9 @@ MackieControlProtocol::~MackieControlProtocol()
        {
                cout << "~MackieControlProtocol caught unknown" << endl;
        }
-       //cout << "finished ~MackieControlProtocol::MackieControlProtocol" << endl;
+#ifdef DEBUG
+       cout << "finished ~MackieControlProtocol::MackieControlProtocol" << endl;
+#endif
 }
 
 Mackie::Surface & MackieControlProtocol::surface()
@@ -131,14 +121,28 @@ Mackie::Surface & MackieControlProtocol::surface()
        return *_surface;
 }
 
-const Mackie::MackiePort & MackieControlProtocol::mcu_port() const
+const Mackie::SurfacePort & MackieControlProtocol::mcu_port() const
 {
-       return dynamic_cast<const MackiePort &>( *_ports[0] );
+       if ( _ports.size() < 1 )
+       {
+               return _dummy_port;
+       }
+       else
+       {
+               return dynamic_cast<const MackiePort &>( *_ports[0] );
+       }
 }
 
-Mackie::MackiePort & MackieControlProtocol::mcu_port()
+Mackie::SurfacePort & MackieControlProtocol::mcu_port()
 {
-       return dynamic_cast<const MackiePort &>( *_ports[0] );
+       if ( _ports.size() < 1 )
+       {
+               return _dummy_port;
+       }
+       else
+       {
+               return dynamic_cast<MackiePort &>( *_ports[0] );
+       }
 }
 
 // go to the previous track.
@@ -275,7 +279,9 @@ void MackieControlProtocol::switch_banks( int initial )
                uint32_t end_pos = min( route_table.size(), sorted.size() );
                Sorted::iterator it = sorted.begin() + _current_initial_bank;
                Sorted::iterator end = sorted.begin() + _current_initial_bank + end_pos;
-               //cout << "switch to " << _current_initial_bank << ", " << end_pos << endl;
+#ifdef DEBUG
+               cout << "switch to " << _current_initial_bank << ", " << end_pos << endl;
+#endif
                
                // link routes to strips
                uint32_t i = 0;
@@ -283,7 +289,9 @@ void MackieControlProtocol::switch_banks( int initial )
                {
                        boost::shared_ptr<Route> route = *it;
                        Strip & strip = *surface().strips[i];
-                       //cout << "remote id " << route->remote_control_id() << " connecting " << route->name() << " to " << strip.name() << " with port " << port_for_id(i) << endl;
+#ifdef DEBUG
+                       cout << "remote id " << route->remote_control_id() << " connecting " << route->name() << " to " << strip.name() << " with port " << port_for_id(i) << endl;
+#endif
                        route_table[i] = route;
                        RouteSignal * rs = new RouteSignal( *route, *this, strip, port_for_id(i) );
                        route_signals.push_back( rs );
@@ -297,51 +305,28 @@ void MackieControlProtocol::switch_banks( int initial )
                {
                        Strip & strip = *surface().strips[i];
                        // send zero for this strip
-                       port_for_id(i).write( builder.zero_strip( strip ) );
+                       MackiePort & port = port_for_id(i);
+                       port.write( builder.zero_strip( port, strip ) );
                }
        }
        
        // display the current start bank.
-       if ( mcu_port().emulation() == MackiePort::bcf2000 )
-       {
-               if ( _current_initial_bank == 0 )
-               {
-                       // send Ar. to 2-char display on the master
-                       mcu_port().write( builder.two_char_display( "Ar", ".." ) );
-               }
-               else
-               {
-                       // write the current first remote_id to the 2-char display
-                       mcu_port().write( builder.two_char_display( _current_initial_bank ) );
-               }
-       }
+       surface().display_bank_start( mcu_port(), builder, _current_initial_bank );
 }
 
 void MackieControlProtocol::zero_all()
 {
-       // TODO turn off 55-char and SMPTE displays
-       
-       if ( mcu_port().emulation() == MackiePort::bcf2000 )
-       {
-               // clear 2-char display
-               mcu_port().write( builder.two_char_display( "LC" ) );
-       }
+       // TODO turn off SMPTE displays
        
        // zero all strips
        for ( Surface::Strips::iterator it = surface().strips.begin(); it != surface().strips.end(); ++it )
        {
-               port_for_id( (*it)->index() ).write( builder.zero_strip( **it ) );
+               MackiePort & port = port_for_id( (*it)->index() );
+               port.write( builder.zero_strip( port, **it ) );
        }
        
        // and the master strip
-       mcu_port().write( builder.zero_strip( master_strip() ) );
-       
-       // and the led ring for the master strip, in bcf mode
-       if ( mcu_port().emulation() == MackiePort::bcf2000 )
-       {
-               Control & control = *surface().controls_by_name["jog"];
-               mcu_port().write( builder.build_led_ring( dynamic_cast<Pot &>( control ), off ) );
-       }
+       mcu_port().write( builder.zero_strip( dynamic_cast<MackiePort&>( mcu_port() ), master_strip() ) );
        
        // turn off global buttons and leds
        // global buttons are only ever on mcu_port, so we don't have
@@ -354,9 +339,12 @@ void MackieControlProtocol::zero_all()
                        mcu_port().write( builder.zero_control( control ) );
                }
        }
+
+       // any hardware-specific stuff
+       surface().zero_all( mcu_port(), builder );
 }
 
-int MackieControlProtocol::set_active (bool yn)
+int MackieControlProtocol::set_active( bool yn )
 {
        if ( yn != _active )
        {
@@ -466,7 +454,7 @@ bool MackieControlProtocol::handle_strip_button( Control & control, ButtonState
        if ( control.name() == "fader_touch" )
        {
                state = bs == press;
-               control.strip().gain().touch( state );
+               control.strip().gain().in_use( state );
        }
        
        return state;
@@ -474,28 +462,50 @@ bool MackieControlProtocol::handle_strip_button( Control & control, ButtonState
 
 void MackieControlProtocol::update_led( Mackie::Button & button, Mackie::LedState ls )
 {
-       MackiePort * port = 0;
-       if ( button.group().is_strip() )
+       if ( ls != none )
        {
-               if ( button.group().is_master() )
+               SurfacePort * port = 0;
+               if ( button.group().is_strip() )
                {
-                       port = &mcu_port();
+                       if ( button.group().is_master() )
+                       {
+                               port = &mcu_port();
+                       }
+                       else
+                       {
+                               port = &port_for_id( dynamic_cast<const Strip&>( button.group() ).index() );
+                       }
                }
                else
                {
-                       port = &port_for_id( dynamic_cast<const Strip&>( button.group() ).index() );
+                       port = &mcu_port();
                }
+               port->write( builder.build_led( button, ls ) );
        }
-       else
+}
+
+void MackieControlProtocol::update_smpte_beats_led()
+{
+       switch ( _timecode_type )
        {
-               port = &mcu_port();
+               case ARDOUR::AnyTime::BBT:
+                       update_global_led( "beats", on );
+                       update_global_led( "smpte", off );
+                       break;
+               case ARDOUR::AnyTime::SMPTE:
+                       update_global_led( "smpte", on );
+                       update_global_led( "beats", off );
+                       break;
+               default:
+                       ostringstream os;
+                       os << "Unknown Anytime::Type " << _timecode_type;
+                       throw runtime_error( os.str() );
        }
-       if ( ls != none ) port->write( builder.build_led( button, ls ) );
 }
 
 void MackieControlProtocol::update_global_button( const string & name, LedState ls )
 {
-       if ( surface().controls_by_name.find( name ) !=surface().controls_by_name.end() )
+       if ( surface().controls_by_name.find( name ) != surface().controls_by_name.end() )
        {
                Button * button = dynamic_cast<Button*>( surface().controls_by_name[name] );
                mcu_port().write( builder.build_led( button->led(), ls ) );
@@ -505,7 +515,22 @@ void MackieControlProtocol::update_global_button( const string & name, LedState
 #ifdef DEBUG
                cout << "Button " << name << " not found" << endl;
 #endif
-               }
+       }
+}
+
+void MackieControlProtocol::update_global_led( const string & name, LedState ls )
+{
+       if ( surface().controls_by_name.find( name ) != surface().controls_by_name.end() )
+       {
+               Led * led = dynamic_cast<Led*>( surface().controls_by_name[name] );
+               mcu_port().write( builder.build_led( *led, ls ) );
+       }
+       else
+       {
+#ifdef DEBUG
+               cout << "Led " << name << " not found" << endl;
+#endif
+       }
 }
 
 // send messages to surface to set controls to correct values
@@ -523,9 +548,13 @@ void MackieControlProtocol::update_surface()
                // update strip from route
                master_route_signal->notify_all();
                
+               // sometimes the jog wheel is a pot
+               surface().blank_jog_ring( mcu_port(), builder );
+               
                // update global buttons and displays
                notify_record_state_changed();
                notify_transport_state_changed();
+               update_smpte_beats_led();
        }
 }
 
@@ -553,31 +582,47 @@ void MackieControlProtocol::connect_session_signals()
 
 void MackieControlProtocol::add_port( MIDI::Port & midi_port, int number )
 {
-       MackiePort * sport = new MackiePort( *this, midi_port, number );
-       _ports.push_back( sport );
-
-       connections_back = sport->init_event.connect(
-               sigc::bind (
-                       mem_fun (*this, &MackieControlProtocol::handle_port_init)
-                       , sport
-               )
-       );
-
-       connections_back = sport->active_event.connect(
-               sigc::bind (
-                       mem_fun (*this, &MackieControlProtocol::handle_port_active)
-                       , sport
-               )
-       );
-
-       connections_back = sport->inactive_event.connect(
-               sigc::bind (
-                       mem_fun (*this, &MackieControlProtocol::handle_port_inactive)
-                       , sport
-               )
-       );
-       
-       _ports_changed = true;
+#ifdef DEBUG
+       cout << "add port " << midi_port.name() << ", " << midi_port.device() << ", " << midi_port.type() << endl;
+       cout << "MIDI::Port::ALSA_Sequencer " << MIDI::Port::ALSA_Sequencer << endl;
+       cout << "MIDI::Port::Unknown " << MIDI::Port::Unknown << endl;
+#endif
+       if ( string( midi_port.device() ) == string( "ardour" ) )
+       {
+               throw MackieControlException( "The Mackie MCU driver will not use a port with device=ardour" );
+       }
+       else if ( midi_port.type() == MIDI::Port::ALSA_Sequencer )
+       {
+               throw MackieControlException( "alsa/sequencer ports don't work with the Mackie MCU driver right now" );
+       }
+       else
+       {
+               MackiePort * sport = new MackiePort( *this, midi_port, number );
+               _ports.push_back( sport );
+               
+               connections_back = sport->init_event.connect(
+                       sigc::bind (
+                               mem_fun (*this, &MackieControlProtocol::handle_port_init)
+                               , sport
+                       )
+               );
+
+               connections_back = sport->active_event.connect(
+                       sigc::bind (
+                               mem_fun (*this, &MackieControlProtocol::handle_port_active)
+                               , sport
+                       )
+               );
+
+               connections_back = sport->inactive_event.connect(
+                       sigc::bind (
+                               mem_fun (*this, &MackieControlProtocol::handle_port_inactive)
+                               , sport
+                       )
+               );
+               
+               _ports_changed = true;
+       }
 }
 
 void MackieControlProtocol::create_ports()
@@ -636,15 +681,23 @@ void MackieControlProtocol::initialize_surface()
        
        set_route_table_size( strips );
        
-       switch ( mcu_port().emulation() )
+       // TODO same as code in mackie_port.cc
+       string emulation = ARDOUR::Config->get_mackie_emulation();
+       if ( emulation == "bcf" )
        {
-               case MackiePort::bcf2000: _surface = new BcfSurface( strips ); break;
-               case MackiePort::mackie: _surface = new MackieSurface( strips ); break;
-               default:
-                       ostringstream os;
-                       os << "no Surface class found for emulation: " << mcu_port().emulation();
-                       throw MackieControlException( os.str() );
+               _surface = new BcfSurface( strips );
+       }
+       else if ( emulation == "mcu" )
+       {
+               _surface = new MackieSurface( strips );
+       }
+       else
+       {
+               ostringstream os;
+               os << "no Surface class found for emulation: " << emulation;
+               throw MackieControlException( os.str() );
        }
+
        _surface->init();
        
        // Connect events. Must be after route table otherwise there will be trouble
@@ -656,6 +709,12 @@ void MackieControlProtocol::initialize_surface()
 
 void MackieControlProtocol::close()
 {
+       // stop polling, and wait for it...
+       // must be before other shutdown otherwise polling loop
+       // calls methods on objects that are deleted
+       _polling = false;
+       pthread_join( thread, 0 );
+       
        // TODO disconnect port active/inactive signals
        // Or at least put a lock here
        
@@ -713,10 +772,6 @@ void MackieControlProtocol::close()
                _surface = 0;
        }
        
-       // stop polling, and wait for it...
-       _polling = false;
-       pthread_join( thread, 0 );
-       
        // shut down MackiePorts
        for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
        {
@@ -737,7 +792,9 @@ void* MackieControlProtocol::_monitor_work (void* arg)
 
 XMLNode & MackieControlProtocol::get_state()
 {
-       //cout << "MackieControlProtocol::get_state" << endl;
+#ifdef DEBUG
+       cout << "MackieControlProtocol::get_state" << endl;
+#endif
        
        // add name of protocol
        XMLNode* node = new XMLNode( X_("Protocol") );
@@ -753,7 +810,9 @@ XMLNode & MackieControlProtocol::get_state()
 
 int MackieControlProtocol::set_state( const XMLNode & node )
 {
-       //cout << "MackieControlProtocol::set_state: active " << _active << endl;
+#ifdef DEBUG
+       cout << "MackieControlProtocol::set_state: active " << _active << endl;
+#endif
        int retval = 0;
        
        // fetch current bank
@@ -780,7 +839,7 @@ int MackieControlProtocol::set_state( const XMLNode & node )
 
 void MackieControlProtocol::handle_control_event( SurfacePort & port, Control & control, const ControlState & state )
 {
-       uint32_t index = control.ordinal() - 1 + ( port.number() * port.strips() );
+       // find the route for the control, if there is one
        boost::shared_ptr<Route> route;
        if ( control.group().is_strip() )
        {
@@ -788,10 +847,14 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control &
                {
                        route = master_route();
                }
-               else if ( index < route_table.size() )
-                       route = route_table[index];
                else
-                       cerr << "Warning: index is " << index << " which is not in the route table, size: " << route_table.size() << endl;
+               {
+                       uint32_t index = control.ordinal() - 1 + ( port.number() * port.strips() );
+                       if ( index < route_table.size() )
+                               route = route_table[index];
+                       else
+                               cerr << "Warning: index is " << index << " which is not in the route table, size: " << route_table.size() << endl;
+               }
        }
        
        // This handles control element events from the surface
@@ -800,30 +863,17 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control &
        switch ( control.type() )
        {
                case Control::type_fader:
-                       if ( control.group().is_strip() )
+                       // find the route in the route table for the id
+                       // if the route isn't available, skip it
+                       // at which point the fader should just reset itself
+                       if ( route != 0 )
                        {
-                               // find the route in the route table for the id
-                               // if the route isn't available, skip it
-                               // at which point the fader should just reset itself
-                               if ( route != 0 )
-                               {
-                                       route->set_gain( slider_position_to_gain( state.pos ), this );
-                                       
-                                       // must echo bytes back to slider now, because
-                                       // the notifier only works if the fader is not being
-                                       // touched. Which it is if we're getting input.
-                                       port.write( builder.build_fader( (Fader&)control, state.pos ) );
-                               }
-                       }
-                       else
-                       {
-                               // master fader
-                               boost::shared_ptr<Route> route = master_route();
-                               if ( route )
-                               {
-                                       route->set_gain( slider_position_to_gain( state.pos ), this );
-                                       port.write( builder.build_fader( (Fader&)control, state.pos ) );
-                               }
+                               route->gain_control()->set_value( state.pos );
+                               
+                               // must echo bytes back to slider now, because
+                               // the notifier only works if the fader is not being
+                               // touched. Which it is if we're getting input.
+                               port.write( builder.build_fader( (Fader&)control, state.pos ) );
                        }
                        break;
                        
@@ -845,9 +895,10 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control &
                        else if ( control.group().is_master() )
                        {
                                // master fader touch
-                               boost::shared_ptr<Route> route = master_route();
-                               if ( route )
+                               if ( route != 0 )
+                               {
                                        handle_strip_button( control, state.button_state, route );
+                               }
                        }
                        else
                        {
@@ -862,19 +913,21 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control &
                        {
                                if ( route != 0 )
                                {
-                                       if ( route->panner().npanners() == 1 )
+                                       // pan for mono input routes, or stereo linked panners
+                                       if ( route->panner().npanners() == 1 || ( route->panner().npanners() == 2 && route->panner().linked() ) )
                                        {
                                                // assume pan for now
-                                               float xpos = route->panner().pan_control(0)->get_value ();
+                                               float xpos;
+                                               route->panner().streampanner (0).get_effective_position (xpos);
                                                
                                                // calculate new value, and trim
-                                               xpos += state.delta;
+                                               xpos += state.delta * state.sign;
                                                if ( xpos > 1.0 )
                                                        xpos = 1.0;
                                                else if ( xpos < 0.0 )
                                                        xpos = 0.0;
                                                
-                                               route->panner().pan_control(0)->set_value( xpos );
+                                               route->panner().streampanner (0).set_position( xpos );
                                        }
                                }
                                else
@@ -885,29 +938,13 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control &
                        }
                        else
                        {
-                               if ( control.name() == "jog" )
+                               if ( control.is_jog() )
                                {
-                                       // TODO use current snap-to setting?
-                                       long delta = state.ticks * 1000;
-                                       nframes_t next = session->transport_frame() + delta;
-                                       if ( delta < 0 && session->transport_frame() < (nframes_t) abs( delta )  )
-                                       {
-                                               next = session->current_start_frame();
-                                       }
-                                       else if ( next > session->current_end_frame() )
-                                       {
-                                               next = session->current_end_frame();
-                                       }
-                                       
-                                       // doesn't work very well
-                                       session->request_locate( next, session->transport_rolling() );
-                                       
-                                       // turn off the led ring, for bcf emulation mode
-                                       port.write( builder.build_led_ring( dynamic_cast<Pot &>( control ), off ) );
+                                       _jog_wheel.jog_event( port, control, state );
                                }
                                else
                                {
-                                       cout << "external controller" << state.ticks << endl;
+                                       cout << "external controller" << state.ticks * state.sign << endl;
                                }
                        }
                        break;
@@ -963,14 +1000,35 @@ void MackieControlProtocol::notify_record_enable_changed( RouteSignal * route_si
        }
 }
 
-void MackieControlProtocol::notify_gain_changed( RouteSignal * route_signal )
+void MackieControlProtocol::notify_active_changed( RouteSignal * route_signal )
+{
+       try
+       {
+#ifdef DEBUG
+               cout << "MackieControlProtocol::notify_active_changed" << endl;
+#endif
+               refresh_current_bank();
+       }
+       catch( exception & e )
+       {
+               cout << e.what() << endl;
+       }
+}
+       
+void MackieControlProtocol::notify_gain_changed( RouteSignal * route_signal, bool force_update )
 {
        try
        {
                Fader & fader = route_signal->strip().gain();
-               if ( !fader.touch() )
+               if ( !fader.in_use() )
                {
-                       route_signal->port().write( builder.build_fader( fader, gain_to_slider_position( route_signal->route().effective_gain() ) ) );
+                       float gain_value = route_signal->route().gain_control()->get_value();
+                       // check that something has actually changed
+                       if ( force_update || gain_value != route_signal->last_gain_written() )
+                       {
+                               route_signal->port().write( builder.build_fader( fader, gain_value ) );
+                               route_signal->last_gain_written( gain_value );
+                       }
                }
        }
        catch( exception & e )
@@ -983,7 +1041,25 @@ void MackieControlProtocol::notify_name_changed( RouteSignal * route_signal )
 {
        try
        {
-               // TODO implement MackieControlProtocol::notify_name_changed
+               Strip & strip = route_signal->strip();
+               if ( !strip.is_master() )
+               {
+                       string line1;
+                       string fullname = route_signal->route().name();
+                       
+                       if ( fullname.length() <= 6 )
+                       {
+                               line1 = fullname;
+                       }
+                       else
+                       {
+                               line1 = PBD::short_version( fullname, 6 );
+                       }
+                       
+                       SurfacePort & port = route_signal->port();
+                       port.write( builder.strip_display( port, strip, 0, line1 ) );
+                       port.write( builder.strip_display_blank( port, strip, 1 ) );
+               }
        }
        catch( exception & e )
        {
@@ -991,17 +1067,27 @@ void MackieControlProtocol::notify_name_changed( RouteSignal * route_signal )
        }
 }
 
-// TODO deal with > 1 channel being panned
-void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal )
+void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal, bool force_update )
 {
        try
        {
                Pot & pot = route_signal->strip().vpot();
-               
-               if ( route_signal->route().panner().npanners() == 1 )
+               const Panner & panner = route_signal->route().panner();
+               if ( panner.npanners() == 1 || ( panner.npanners() == 2 && panner.linked() ) )
                {
-                       float pos = route_signal->route().panner().pan_control(0)->get_value();
-                       route_signal->port().write( builder.build_led_ring( pot, ControlState( on, pos ) ) );
+                       float pos;
+                       route_signal->route().panner().streampanner(0).get_effective_position( pos );
+                       
+                       // cache the MidiByteArray here, because the mackie led control is much lower
+                       // resolution than the panner control. So we save lots of byte
+                       // sends in spite of more work on the comparison
+                       MidiByteArray bytes = builder.build_led_ring( pot, ControlState( on, pos ), MackieMidiBuilder::midi_pot_mode_dot );
+                       // check that something has actually changed
+                       if ( force_update || bytes != route_signal->last_pan_written() )
+                       {
+                               route_signal->port().write( bytes );
+                               route_signal->last_pan_written( bytes );
+                       }
                }
                else
                {
@@ -1017,39 +1103,18 @@ void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal )
 // TODO handle plugin automation polling
 void MackieControlProtocol::update_automation( RouteSignal & rs )
 {
-       ARDOUR::AutoState gain_state = rs.route().gain_control()->alist()->automation_state();
+       ARDOUR::AutoState gain_state = rs.route().gain_control()->automation_state();
        if ( gain_state == Touch || gain_state == Play )
        {
-               notify_gain_changed( &rs );
+               notify_gain_changed( &rs, false );
        }
        
        ARDOUR::AutoState panner_state = rs.route().panner().automation_state();
        if ( panner_state == Touch || panner_state == Play )
        {
-               notify_panner_changed( &rs );
-       }
-}
-
-void MackieControlProtocol::poll_automation ()
-{
-       if ( _active && _automation_last.elapsed() >= 20 )
-       {
-               // do all currently mapped routes
-               for( RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it )
-               {
-                       update_automation( **it );
-               }
-               
-               // and the master strip
-               if ( master_route_signal != 0 )
-               {
-                       update_automation( *master_route_signal );
-               }
-               
-               update_timecode_display();
-               
-               _automation_last.start();
+               notify_panner_changed( &rs, false );
        }
+       _automation_last.start();
 }
 
 string MackieControlProtocol::format_bbt_timecode( nframes_t now_frame )
@@ -1130,6 +1195,28 @@ void MackieControlProtocol::update_timecode_display()
        }
 }
 
+void MackieControlProtocol::poll_session_data()
+{
+       if ( _active && _automation_last.elapsed() >= 20 )
+       {
+               // do all currently mapped routes
+               for( RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it )
+               {
+                       update_automation( **it );
+               }
+               
+               // and the master strip
+               if ( master_route_signal != 0 )
+               {
+                       update_automation( *master_route_signal );
+               }
+               
+               update_timecode_display();
+               
+               _automation_last.start();
+       }
+}
+
 /////////////////////////////////////
 // Transport Buttons
 /////////////////////////////////////
@@ -1137,10 +1224,28 @@ void MackieControlProtocol::update_timecode_display()
 LedState MackieControlProtocol::frm_left_press( Button & button )
 {
        // can use first_mark_before/after as well
+       unsigned long elapsed = _frm_left_last.restart();
+       
        Location * loc = session->locations()->first_location_before (
                session->transport_frame()
        );
-       if ( loc != 0 ) session->request_locate( loc->start(), session->transport_rolling() );
+       
+       // allow a quick double to go past a previous mark 
+       if ( session->transport_rolling() && elapsed < 500 && loc != 0 )
+       {
+               Location * loc_two_back = session->locations()->first_location_before ( loc->start() );
+               if ( loc_two_back != 0 )
+               {
+                       loc = loc_two_back;
+               }
+       }
+       
+       // move to the location, if it's valid
+       if ( loc != 0 )
+       {
+               session->request_locate( loc->start(), session->transport_rolling() );
+       }
+       
        return on;
 }
 
@@ -1210,12 +1315,16 @@ LedState MackieControlProtocol::record_release( Button & button )
 
 LedState MackieControlProtocol::rewind_press( Button & button )
 {
-       session->request_transport_speed( -4.0 );
+       _jog_wheel.push( JogWheel::speed );
+       _jog_wheel.transport_direction( -1 );
+       session->request_transport_speed( -_jog_wheel.transport_speed() );
        return on;
 }
 
 LedState MackieControlProtocol::rewind_release( Button & button )
 {
+       _jog_wheel.pop();
+       _jog_wheel.transport_direction( 0 );
        if ( _transport_previously_rolling )
                session->request_transport_speed( 1.0 );
        else
@@ -1225,12 +1334,16 @@ LedState MackieControlProtocol::rewind_release( Button & button )
 
 LedState MackieControlProtocol::ffwd_press( Button & button )
 {
-       session->request_transport_speed( 4.0 );
+       _jog_wheel.push( JogWheel::speed );
+       _jog_wheel.transport_direction( 1 );
+       session->request_transport_speed( _jog_wheel.transport_speed() );
        return on;
 }
 
 LedState MackieControlProtocol::ffwd_release( Button & button )
 {
+       _jog_wheel.pop();
+       _jog_wheel.transport_direction( 0 );
        if ( _transport_previously_rolling )
                session->request_transport_speed( 1.0 );
        else
@@ -1238,6 +1351,87 @@ LedState MackieControlProtocol::ffwd_release( Button & button )
        return off;
 }
 
+LedState MackieControlProtocol::loop_press( Button & button )
+{
+       session->request_play_loop( !session->get_play_loop() );
+       return on;
+}
+
+LedState MackieControlProtocol::loop_release( Button & button )
+{
+       return session->get_play_loop();
+}
+
+LedState MackieControlProtocol::punch_in_press( Button & button )
+{
+       bool state = !Config->get_punch_in();
+       Config->set_punch_in( state );
+       return state;
+}
+
+LedState MackieControlProtocol::punch_in_release( Button & button )
+{
+       return Config->get_punch_in();
+}
+
+LedState MackieControlProtocol::punch_out_press( Button & button )
+{
+       bool state = !Config->get_punch_out();
+       Config->set_punch_out( state );
+       return state;
+}
+
+LedState MackieControlProtocol::punch_out_release( Button & button )
+{
+       return Config->get_punch_out();
+}
+
+LedState MackieControlProtocol::home_press( Button & button )
+{
+       session->goto_start();
+       return on;
+}
+
+LedState MackieControlProtocol::home_release( Button & button )
+{
+       return off;
+}
+
+LedState MackieControlProtocol::end_press( Button & button )
+{
+       session->goto_end();
+       return on;
+}
+
+LedState MackieControlProtocol::end_release( Button & button )
+{
+       return off;
+}
+
+LedState MackieControlProtocol::clicking_press( Button & button )
+{
+       bool state = !Config->get_clicking();
+       Config->set_clicking( state );
+       return state;
+}
+
+LedState MackieControlProtocol::clicking_release( Button & button )
+{
+       return Config->get_clicking();
+}
+
+LedState MackieControlProtocol::global_solo_press( Button & button )
+{
+       bool state = !session->soloing();
+       session->set_all_solo ( state );
+       return state;
+}
+
+LedState MackieControlProtocol::global_solo_release( Button & button )
+{
+       return session->soloing();
+}
+
 ///////////////////////////////////////////
 // Session signals
 ///////////////////////////////////////////
@@ -1333,87 +1527,6 @@ void MackieControlProtocol::notify_transport_state_changed()
        mcu_port().write( builder.build_led( *rec, record_release( *rec ) ) );
 }
 
-LedState MackieControlProtocol::loop_press( Button & button )
-{
-       session->request_play_loop( !session->get_play_loop() );
-       return on;
-}
-
-LedState MackieControlProtocol::loop_release( Button & button )
-{
-       return session->get_play_loop();
-}
-
-LedState MackieControlProtocol::punch_in_press( Button & button )
-{
-       bool state = !Config->get_punch_in();
-       Config->set_punch_in( state );
-       return state;
-}
-
-LedState MackieControlProtocol::punch_in_release( Button & button )
-{
-       return Config->get_punch_in();
-}
-
-LedState MackieControlProtocol::punch_out_press( Button & button )
-{
-       bool state = !Config->get_punch_out();
-       Config->set_punch_out( state );
-       return state;
-}
-
-LedState MackieControlProtocol::punch_out_release( Button & button )
-{
-       return Config->get_punch_out();
-}
-
-LedState MackieControlProtocol::home_press( Button & button )
-{
-       session->goto_start();
-       return on;
-}
-
-LedState MackieControlProtocol::home_release( Button & button )
-{
-       return off;
-}
-
-LedState MackieControlProtocol::end_press( Button & button )
-{
-       session->goto_end();
-       return on;
-}
-
-LedState MackieControlProtocol::end_release( Button & button )
-{
-       return off;
-}
-
-LedState MackieControlProtocol::clicking_press( Button & button )
-{
-       bool state = !Config->get_clicking();
-       Config->set_clicking( state );
-       return state;
-}
-
-LedState MackieControlProtocol::clicking_release( Button & button )
-{
-       return Config->get_clicking();
-}
-
-LedState MackieControlProtocol::global_solo_press( Button & button )
-{
-       bool state = !session->soloing();
-       session->set_all_solo ( state );
-       return state;
-}
-
-LedState MackieControlProtocol::global_solo_release( Button & button )
-{
-       return session->soloing();
-}
-
 /////////////////////////////////////
 // Bank Switching
 /////////////////////////////////////
@@ -1530,3 +1643,96 @@ LedState MackieControlProtocol::marker_release( Button & button )
 {
        return off;
 }
+
+void jog_wheel_state_display( JogWheel::State state, SurfacePort & port )
+{
+       switch( state )
+       {
+               case JogWheel::zoom: port.write( builder.two_char_display( "Zm" ) ); break;
+               case JogWheel::scroll: port.write( builder.two_char_display( "Sc" ) ); break;
+               case JogWheel::scrub: port.write( builder.two_char_display( "Sb" ) ); break;
+               case JogWheel::shuttle: port.write( builder.two_char_display( "Sh" ) ); break;
+               case JogWheel::speed: port.write( builder.two_char_display( "Sp" ) ); break;
+               case JogWheel::select: port.write( builder.two_char_display( "Se" ) ); break;
+       }
+}
+
+Mackie::LedState MackieControlProtocol::zoom_press( Mackie::Button & )
+{
+       _jog_wheel.zoom_state_toggle();
+       update_global_button( "scrub", _jog_wheel.jog_wheel_state() == JogWheel::scrub );
+       jog_wheel_state_display( _jog_wheel.jog_wheel_state(), mcu_port() );
+       return _jog_wheel.jog_wheel_state() == JogWheel::zoom;
+}
+
+Mackie::LedState MackieControlProtocol::zoom_release( Mackie::Button & )
+{
+       return _jog_wheel.jog_wheel_state() == JogWheel::zoom;
+}
+
+Mackie::LedState MackieControlProtocol::scrub_press( Mackie::Button & )
+{
+       _jog_wheel.scrub_state_cycle();
+       update_global_button( "zoom", _jog_wheel.jog_wheel_state() == JogWheel::zoom );
+       jog_wheel_state_display( _jog_wheel.jog_wheel_state(), mcu_port() );
+       return
+               _jog_wheel.jog_wheel_state() == JogWheel::scrub
+               ||
+               _jog_wheel.jog_wheel_state() == JogWheel::shuttle
+       ;
+}
+
+Mackie::LedState MackieControlProtocol::scrub_release( Mackie::Button & )
+{
+       return
+               _jog_wheel.jog_wheel_state() == JogWheel::scrub
+               ||
+               _jog_wheel.jog_wheel_state() == JogWheel::shuttle
+       ;
+}
+
+LedState MackieControlProtocol::drop_press( Button & button )
+{
+       session->remove_last_capture();
+       return on;
+}
+
+LedState MackieControlProtocol::drop_release( Button & button )
+{
+       return off;
+}
+
+LedState MackieControlProtocol::save_press( Button & button )
+{
+       session->save_state( "" );
+       return on;
+}
+
+LedState MackieControlProtocol::save_release( Button & button )
+{
+       return off;
+}
+
+LedState MackieControlProtocol::smpte_beats_press( Button & )
+{
+       switch ( _timecode_type )
+       {
+               case ARDOUR::AnyTime::BBT:
+                       _timecode_type = ARDOUR::AnyTime::SMPTE;
+                       break;
+               case ARDOUR::AnyTime::SMPTE:
+                       _timecode_type = ARDOUR::AnyTime::BBT;
+                       break;
+               default:
+                       ostringstream os;
+                       os << "Unknown Anytime::Type " << _timecode_type;
+                       throw runtime_error( os.str() );
+       }
+       update_smpte_beats_led();
+       return on;
+}
+
+LedState MackieControlProtocol::smpte_beats_release( Button & )
+{
+       return off;
+}
index 735a2c88bd7b26b06538f350e9a5dbacff402032..4e4a76f977a15329cb065665bda4b2a704fa6cf6 100644 (file)
 #include <control_protocol/control_protocol.h>
 #include "midi_byte_array.h"
 #include "controls.h"
+#include "dummy_port.h"
 #include "route_signal.h"
 #include "mackie_button_handler.h"
 #include "mackie_port.h"
+#include "mackie_jog_wheel.h"
+#include "timer.h"
 
 namespace MIDI {
        class Port;
@@ -93,14 +96,16 @@ class MackieControlProtocol
        /// Signal handler for Route::record_enable_changed
        void notify_record_enable_changed( Mackie::RouteSignal * );
        /// Signal handler for Route::gain_changed ( from IO )
-       void notify_gain_changed( Mackie::RouteSignal * );
+       void notify_gain_changed( Mackie::RouteSignal *, bool force_update = true );
        /// Signal handler for Route::name_change
        void notify_name_changed( Mackie::RouteSignal * );
        /// Signal handler from Panner::Change
-       void notify_panner_changed( Mackie::RouteSignal * );
+       void notify_panner_changed( Mackie::RouteSignal *, bool force_update = true );
        /// Signal handler for new routes added
        void notify_route_added( ARDOUR::Session::RouteList & );
-
+       /// Signal handler for Route::active_changed
+       void notify_active_changed( Mackie::RouteSignal * );
        void notify_remote_id_changed();
 
        /// rebuild the current bank. Called on route added/removed and
@@ -116,12 +121,15 @@ class MackieControlProtocol
        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.
+       /// Turn smpte on and beats off, or vice versa, depending
+       /// on state of _timecode_type
+       void update_smpte_beats_led();
+  
+       /// 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 );
+       void update_global_led( const std::string & name, Mackie::LedState );
   
    // transport button handler methods from MackieButtonHandler
        virtual Mackie::LedState frm_left_press( Mackie::Button & );
@@ -183,6 +191,28 @@ class MackieControlProtocol
        virtual Mackie::LedState marker_press( Mackie::Button & );
        virtual Mackie::LedState marker_release( Mackie::Button & );
 
+       virtual Mackie::LedState drop_press( Mackie::Button & );
+       virtual Mackie::LedState drop_release( Mackie::Button & );
+
+       virtual Mackie::LedState save_press( Mackie::Button & );
+       virtual Mackie::LedState save_release( Mackie::Button & );
+
+       virtual Mackie::LedState smpte_beats_press( Mackie::Button & );
+       virtual Mackie::LedState smpte_beats_release( Mackie::Button & );
+
+       // jog wheel states
+       virtual Mackie::LedState zoom_press( Mackie::Button & );
+       virtual Mackie::LedState zoom_release( Mackie::Button & );
+
+       virtual Mackie::LedState scrub_press( Mackie::Button & );
+       virtual Mackie::LedState scrub_release( Mackie::Button & );
+       
+   /// This is the main MCU port, ie not an extender port
+       /// Only for use by JogWheel
+       const Mackie::SurfacePort & mcu_port() const;
+       Mackie::SurfacePort & mcu_port();
+       ARDOUR::Session & get_session() { return *session; }
   protected:
        // create instances of MackiePort, depending on what's found in ardour.rc
        void create_ports();
@@ -221,10 +251,6 @@ class MackieControlProtocol
    // 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;
        
@@ -260,14 +286,17 @@ class MackieControlProtocol
                automation from the currently active routes and
                timecode displays.
        */
-       void poll_automation ();
+       void poll_session_data();
        
        // called from poll_automation to figure out which automations need to be sent
        void update_automation( Mackie::RouteSignal & );
-
+       
        // also called from poll_automation to update timecode display
        void update_timecode_display();
 
+       std::string format_bbt_timecode( nframes_t now_frame );
+       std::string format_smpte_timecode( nframes_t now_frame );
+       
        /**
                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
@@ -293,6 +322,9 @@ class MackieControlProtocol
        typedef vector<Mackie::MackiePort*> MackiePorts;
        MackiePorts _ports;
   
+       /// Sometimes the real port goes away, and we want to contain the breakage
+       Mackie::DummyPort _dummy_port;
+  
    // the thread that polls the ports for incoming midi data
        pthread_t thread;
   
@@ -321,6 +353,20 @@ class MackieControlProtocol
        int nfds;
        
        bool _transport_previously_rolling;
+       
+       // timer for two quick marker left presses
+       Mackie::Timer _frm_left_last;
+       
+       Mackie::JogWheel _jog_wheel;
+       
+       // Timer for controlling midi bandwidth used by automation polls
+       Mackie::Timer _automation_last;
+       
+       // last written timecode string
+       std::string _timecode_last;
+       
+       // Which timecode are we displaying? BBT or SMPTE
+       ARDOUR::AnyTime::Type _timecode_type;
 };
 
 #endif // ardour_mackie_control_protocol_h
index cd95551f7021e6a8aa37065a482bb997ea573a7e..88c00ed6eb225dd0d99685bbb675d94a52234aef 100644 (file)
@@ -28,7 +28,15 @@ const char * MackieControlProtocol::default_port_name = "mcu";
 
 bool MackieControlProtocol::probe()
 {
-       return MIDI::Manager::instance()->port( default_port_name ) != 0;
+       if ( MIDI::Manager::instance()->port( default_port_name ) == 0 )
+       {
+               error << "No port called mcu. Add it to ardour.rc." << endmsg;
+               return false;
+       }
+       else
+       {
+               return true;
+       }
 }
 
 void * MackieControlProtocol::monitor_work()
@@ -52,8 +60,8 @@ void * MackieControlProtocol::monitor_work()
                                        update_ports();
                                }
                        }
-                       // poll for automation data from the routes
-                       poll_automation();
+                       // poll for session data that needs to go to the unit
+                       poll_session_data();
                }
                catch ( exception & e )
                {
@@ -71,30 +79,51 @@ void * MackieControlProtocol::monitor_work()
 
 void MackieControlProtocol::update_ports()
 {
+#ifdef DEBUG
+       cout << "MackieControlProtocol::update_ports" << endl;
+#endif
        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
+#ifdef DEBUG
+               cout << "MackieControlProtocol::update_ports lock acquired" << endl;
+#endif
                if ( _ports_changed )
                {
                        // create new pollfd structures
-                       if ( pfd != 0 ) delete[] pfd;
-                       // TODO This might be a memory leak. How does thread cancellation cleanup work?
+                       if ( pfd != 0 )
+                       {
+                               delete[] pfd;
+                               pfd = 0;
+                       }
                        pfd = new pollfd[_ports.size()];
+#ifdef DEBUG
+                       cout << "pfd: " << pfd << endl;
+#endif
                        nfds = 0;
-
                        for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
                        {
-                               //cout << "adding port " << (*it)->port().name() << " to pollfd" << endl;
+                               // add the port any handler
+                               (*it)->connect_any();
+#ifdef DEBUG
+                               cout << "adding pollfd for port " << (*it)->port().name() << " to pollfd " << nfds << endl;
+#endif
                                pfd[nfds].fd = (*it)->port().selectable();
                                pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
                                ++nfds;
                        }
                        _ports_changed = false;
                }
+#ifdef DEBUG
+               cout << "MackieControlProtocol::update_ports signal" << endl;
+#endif
                update_cond.signal();
        }
+#ifdef DEBUG
+       cout << "MackieControlProtocol::update_ports finish" << endl;
+#endif
 }
 
 void MackieControlProtocol::read_ports()
@@ -127,12 +156,14 @@ bool MackieControlProtocol::poll_ports()
        if ( nfds < 1 )
        {
                lock.release();
-               //cout << "poll_ports no ports" << endl;
+#ifdef DEBUG
+               cout << "poll_ports no ports" << endl;
+#endif
                usleep( no_ports_sleep * 1000 );
                return false;
        }
 
-       int retval = poll( pfd, nfds, timeout );
+       int retval = ::poll( pfd, nfds, timeout );
        if ( retval < 0 )
        {
                // gdb at work, perhaps
@@ -179,14 +210,21 @@ void MackieControlProtocol::handle_port_active( SurfacePort * port )
        // finally update session state to the surface
        // TODO but this is also done in set_active, and
        // in fact update_surface won't execute unless
+#ifdef DEBUG
+       cout << "update_surface in handle_port_active" << endl;
+#endif
        // _active == true
-       //cout << "update_surface in handle_port_active" << endl;
        update_surface();
 }
 
 void MackieControlProtocol::handle_port_init( Mackie::SurfacePort * sport )
 {
-       //cout << "MackieControlProtocol::handle_port_init" << endl;
+#ifdef DEBUG
+       cout << "MackieControlProtocol::handle_port_init" << endl;
+#endif
        _ports_changed = true;
        update_ports();
+#ifdef DEBUG
+       cout << "MackieControlProtocol::handle_port_init finish" << endl;
+#endif
 }
diff --git a/libs/surfaces/mackie/mackie_jog_wheel.cc b/libs/surfaces/mackie/mackie_jog_wheel.cc
new file mode 100644 (file)
index 0000000..d05eb23
--- /dev/null
@@ -0,0 +1,194 @@
+#include <cmath>
+
+#include "mackie_jog_wheel.h"
+
+#include "mackie_control_protocol.h"
+#include "surface_port.h"
+#include "controls.h"
+#include "surface.h"
+
+#include <algorithm>
+
+using namespace Mackie;
+using std::isnan;
+
+JogWheel::JogWheel( MackieControlProtocol & mcp )
+: _mcp( mcp )
+, _transport_speed( 4.0 )
+, _transport_direction( 0 )
+, _shuttle_speed( 0.0 )
+{
+}
+
+JogWheel::State JogWheel::jog_wheel_state() const
+{
+       if ( !_jog_wheel_states.empty() )
+               return _jog_wheel_states.top();
+       else 
+               return scroll;
+}
+
+void JogWheel::zoom_event( SurfacePort & port, Control & control, const ControlState & state )
+{
+}
+
+void JogWheel::scrub_event( SurfacePort & port, Control & control, const ControlState & state )
+{
+}
+
+void JogWheel::speed_event( SurfacePort & port, Control & control, const ControlState & state )
+{
+}
+
+void JogWheel::scroll_event( SurfacePort & port, Control & control, const ControlState & state )
+{
+}
+
+void JogWheel::jog_event( SurfacePort & port, Control & control, const ControlState & state )
+{
+       // TODO use current snap-to setting?
+       switch ( jog_wheel_state() )
+       {
+       case scroll:
+               _mcp.ScrollTimeline( state.delta * state.sign );
+               break;
+       
+       case zoom:
+               // Chunky Zoom.
+               // TODO implement something similar to ScrollTimeline which
+               // ends up in Editor::control_scroll for smoother zooming.
+               if ( state.sign > 0 )
+                       for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomIn();
+               else
+                       for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomOut();
+               break;
+               
+       case speed:
+               // locally, _transport_speed is an positive value
+               _transport_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
+
+               // make sure no weirdness gets to the session
+               if ( _transport_speed < 0 || isnan( _transport_speed ) )
+               {
+                       _transport_speed = 0.0;
+               }
+               
+               // translate _transport_speed speed to a signed transport velocity
+               _mcp.get_session().request_transport_speed( transport_speed() * transport_direction() );
+               break;
+       
+       case scrub:
+       {
+               if ( state.sign != 0 )
+               {
+                       add_scrub_interval( _scrub_timer.restart() );
+                       // x clicks per second => speed == 1.0
+                       float speed = _mcp.surface().scrub_scaling_factor() / average_scrub_interval() * state.ticks;
+                       _mcp.get_session().request_transport_speed( speed * state.sign );
+               }
+               else
+               {
+                       // we have a stop event
+                       check_scrubbing();
+               }
+               break;
+       }
+       
+       case shuttle:
+               _shuttle_speed = _mcp.get_session().transport_speed();
+               _shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
+               _mcp.get_session().request_transport_speed( _shuttle_speed );
+               break;
+       
+       case select:
+               cout << "JogWheel select not implemented" << endl;
+               break;
+       }
+}
+
+void JogWheel::check_scrubbing()
+{
+       // if the last elapsed is greater than the average + std deviation, then stop
+       if ( !_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval() )
+       {
+               _mcp.get_session().request_transport_speed( 0.0 );
+               _scrub_intervals.clear();
+       }
+}
+
+void JogWheel::push( State state )
+{
+       _jog_wheel_states.push( state );
+}
+
+void JogWheel::pop()
+{
+       if ( _jog_wheel_states.size() > 0 )
+       {
+               _jog_wheel_states.pop();
+       }
+}
+
+void JogWheel::zoom_state_toggle()
+{
+       if ( jog_wheel_state() == zoom )
+               pop();
+       else
+               push( zoom );
+}
+
+JogWheel::State JogWheel::scrub_state_cycle()
+{
+       State top = jog_wheel_state();
+       if ( top == scrub )
+       {
+               // stop scrubbing and go to shuttle
+               pop();
+               push( shuttle );
+               _shuttle_speed = 0.0;
+       }
+       else if ( top == shuttle )
+       {
+               // default to scroll, or the last selected
+               pop();
+       }
+       else
+       {
+               // start with scrub
+               push( scrub );
+       }
+       
+       return jog_wheel_state();
+}
+
+void JogWheel::add_scrub_interval( unsigned long elapsed )
+{
+       if ( _scrub_intervals.size() > 5 )
+       {
+               _scrub_intervals.pop_front();
+       }
+       _scrub_intervals.push_back( elapsed );
+}
+
+float JogWheel::average_scrub_interval()
+{
+       float sum = 0.0;
+       for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
+       {
+               sum += *it;
+       }
+       return sum / _scrub_intervals.size(); 
+}
+
+float JogWheel::std_dev_scrub_interval()
+{
+       float average = average_scrub_interval();
+       
+       // calculate standard deviation
+       float sum = 0.0;
+       for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
+       {
+               sum += pow( *it - average, 2 );
+       }
+       return sqrt( sum / _scrub_intervals.size() -1 );
+}
diff --git a/libs/surfaces/mackie/mackie_jog_wheel.h b/libs/surfaces/mackie/mackie_jog_wheel.h
new file mode 100644 (file)
index 0000000..83a4364
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef mackie_jog_wheel
+#define mackie_jog_wheel
+
+#include "timer.h"
+
+#include <stack>
+#include <deque>
+#include <queue>
+
+class MackieControlProtocol;
+
+namespace Mackie
+{
+
+class SurfacePort;
+class Control;
+class ControlState;
+
+/**
+       A jog wheel can be used to control many things. This
+       handles all of the states and state transitions.
+       
+       Mainly it exists to avoid putting a bunch of messy
+       stuff in MackieControlProtocol.
+       
+       But it doesn't really know who it is, with stacks, queues and various
+       boolean state variables.
+*/
+class JogWheel
+{
+public:
+       enum State { scroll, zoom, speed, scrub, shuttle, select };
+       
+       JogWheel( MackieControlProtocol & mcp );
+
+       /// As the wheel turns...
+       void jog_event( SurfacePort & port, Control & control, const ControlState & state );
+       
+       // These are for incoming button presses that change the internal state
+       // but they're not actually used at the moment.
+       void zoom_event( SurfacePort & port, Control & control, const ControlState & state );
+       void scrub_event( SurfacePort & port, Control & control, const ControlState & state );
+       void speed_event( SurfacePort & port, Control & control, const ControlState & state );
+       void scroll_event( SurfacePort & port, Control & control, const ControlState & state );
+
+       /// Return the current jog wheel mode, which defaults to Scroll
+       State jog_wheel_state() const;
+       
+       /// The current transport speed for ffwd and rew. Can be
+       /// set by wheel when they're pressed.
+       float transport_speed() const { return _transport_speed; }
+       
+       /// one of -1,0,1
+       int transport_direction() const { return _transport_direction; }
+       void transport_direction( int rhs ) { _transport_direction = rhs; }
+       
+       void push( State state );
+       void pop();
+       
+       /// Turn zoom mode on and off
+       void zoom_state_toggle();
+       
+       /**
+               Cycle scrub -> shuttle -> previous
+       */
+       State scrub_state_cycle();
+       
+       /// Check to see when the last scrub event was
+       /// And stop scrubbing if it was too long ago.
+       /// Intended to be called from a periodic timer of 
+       /// some kind.
+       void check_scrubbing();
+
+protected:
+       void add_scrub_interval( unsigned long elapsed );
+       float average_scrub_interval();
+       float std_dev_scrub_interval();
+
+private:
+       MackieControlProtocol & _mcp;
+
+       /// transport speed for ffwd and rew, controller by jog
+       float _transport_speed;
+       int _transport_direction;
+
+       /// Speed for shuttle
+       float _shuttle_speed;
+       
+       /// a stack for keeping track of states
+       std::stack<State> _jog_wheel_states;
+
+       /// So we know how fast to set the transport speed while scrubbing
+       Timer _scrub_timer;
+
+       /// to keep track of what the current scrub rate is
+       /// so we can calculate a moving average
+       std::deque<unsigned long> _scrub_intervals;
+};
+
+}
+
+#endif
index 8ed98a57205a4e7fbb373d4508386eb23e2d1e80..89a6ce7789d8963111093254225165e8d5413007 100644 (file)
 #include <typeinfo>
 #include <sstream>
 #include <iomanip>
+#include <algorithm>
 
 #include "controls.h"
 #include "midi_byte_array.h"
+#include "mackie_port.h"
 
 using namespace Mackie;
 using namespace std;
@@ -44,12 +46,12 @@ MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const Con
        return retval;
 }
 
-MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state )
+MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state, midi_pot_mode mode  )
 {
-       return build_led_ring( pot.led_ring(), state );
+       return build_led_ring( pot.led_ring(), state, mode );
 }
 
-MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state )
+MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state, midi_pot_mode mode )
 {
        // The other way of doing this:
        // 0x30 + pot/ring number (0-7)
@@ -58,9 +60,9 @@ MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const
                // the control type
                , midi_pot_id
                // the id
-               , 0x20 + led_ring.id()
+               , 0x20 + led_ring.raw_id()
                // the value
-               , calculate_pot_value( midi_pot_mode_dot, state )
+               , calculate_pot_value( mode, state )
        );
 }
 
@@ -82,7 +84,7 @@ MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
        
        return MidiByteArray ( 3
                , midi_button_id
-               , led.id()
+               , led.raw_id()
                , state
        );
 }
@@ -92,7 +94,7 @@ MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
        int posi = int( 0x3fff * pos );
        
        return MidiByteArray ( 3
-               , midi_fader_id | fader.id()
+               , midi_fader_id | fader.raw_id()
                // lower-order bits
                , posi & 0x7f
                // higher-order bits
@@ -100,7 +102,7 @@ MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
        );
 }
 
-MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
+MidiByteArray MackieMidiBuilder::zero_strip( SurfacePort & port, const Strip & strip )
 {
        Group::Controls::const_iterator it = strip.controls().begin();
        MidiByteArray retval;
@@ -110,6 +112,10 @@ MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
                if ( control.accepts_feedback() )
                        retval << zero_control( control );
        }
+       
+       // These must have sysex headers
+       retval << strip_display_blank( port, strip, 0 );
+       retval << strip_display_blank( port, strip, 1 );
        return retval;
 }
 
@@ -171,3 +177,98 @@ MidiByteArray MackieMidiBuilder::two_char_display( unsigned int value, const std
        os << setfill('0') << setw(2) << value % 100;
        return two_char_display( os.str() );
 }
+
+MidiByteArray MackieMidiBuilder::strip_display_blank( SurfacePort & port, const Strip & strip, unsigned int line_number )
+{
+       // 6 spaces, not 7 because strip_display adds a space where appropriate
+       return strip_display( port, strip, line_number, "      " );
+}
+
+MidiByteArray MackieMidiBuilder::strip_display( SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line )
+{
+       if ( line_number > 1 )
+       {
+               throw runtime_error( "line_number must be 0 or 1" );
+       }
+       
+       if ( strip.index() > 7 )
+       {
+               throw runtime_error( "strip.index() must be between 0 and 7" );
+       }
+
+#ifdef DEBUG   
+       cout << "MackieMidiBuilder::strip_display index: " << strip.index() << ", line " << line_number << ": " << line << endl;
+#endif
+
+       MidiByteArray retval;
+       
+       // sysex header
+       retval << port.sysex_hdr();
+       
+       // code for display
+       retval << 0x12;
+       // offset (0 to 0x37 first line, 0x38 to 0x6f for second line )
+       retval << ( strip.index() * 7 + ( line_number * 0x38 ) );
+       
+       // ascii data to display
+       retval << line;
+       // pad with " " out to 6 chars
+       for ( int i = line.length(); i < 6; ++i ) retval << ' ';
+       
+       // column spacer, unless it's the right-hand column
+       if ( strip.index() < 7 ) retval << ' ';
+
+       // sysex trailer
+       retval << MIDI::eox;
+       
+#ifdef DEBUG   
+       cout << "MackieMidiBuilder::strip_display midi: " << retval << endl;
+#endif
+       return retval;
+}
+       
+MidiByteArray MackieMidiBuilder::all_strips_display( SurfacePort & port, std::vector<std::string> & lines1, std::vector<std::string> & lines2 )
+{
+       MidiByteArray retval;
+       retval << 0x12 << 0;
+       // NOTE remember max 112 bytes per message, including sysex headers
+       retval << "Not working yet";
+       return retval;
+}
+
+MidiByteArray MackieMidiBuilder::timecode_display( SurfacePort & port, const std::string & timecode, const std::string & last_timecode )
+{
+       // if there's no change, send nothing, not even sysex header
+       if ( timecode == last_timecode ) return MidiByteArray();
+       
+       // length sanity checking
+       string local_timecode = timecode;
+       // truncate to 10 characters
+       if ( local_timecode.length() > 10 ) local_timecode = local_timecode.substr( 0, 10 );
+       // pad to 10 characters
+       while ( local_timecode.length() < 10 ) local_timecode += " ";
+               
+       // find the suffix of local_timecode that differs from last_timecode
+       std::pair<string::const_iterator,string::iterator> pp = mismatch( last_timecode.begin(), last_timecode.end(), local_timecode.begin() );
+       
+       MidiByteArray retval;
+       
+       // sysex header
+       retval << port.sysex_hdr();
+       
+       // code for timecode display
+       retval << 0x10;
+       
+       // translate characters. These are sent in reverse order of display
+       // hence the reverse iterators
+       string::reverse_iterator rend = reverse_iterator<string::iterator>( pp.second );
+       for ( string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it )
+       {
+               retval << translate_seven_segment( *it );
+       }
+       
+       // sysex trailer
+       retval << MIDI::eox;
+       
+       return retval;
+}
index f0c3d51a54d03439edc44eed0ceccd449bc836a5..b5fc9f73c95b7feada8b7b936be7fd5f00282261 100644 (file)
 
 #include "midi_byte_array.h"
 #include "types.h"
+#include "controls.h"
 
 namespace Mackie
 {
 
+class SurfacePort;
+
 /**
        This knows how to build midi messages given a control and
        a state.
@@ -37,9 +40,9 @@ public:
                with the control id
        */
        enum midi_types {
-               midi_fader_id = 0xe0
-               , midi_button_id = 0x90
-               , midi_pot_id = 0xb0
+               midi_fader_id = Control::type_fader
+               , midi_button_id = Control::type_button
+               , midi_pot_id = Control::type_pot
        };
 
        /**
@@ -52,8 +55,8 @@ public:
                , 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_ring( const Pot & pot, const ControlState &, midi_pot_mode mode = midi_pot_mode_dot );
+       MidiByteArray build_led_ring( const LedRing & led_ring, const ControlState &, midi_pot_mode mode = midi_pot_mode_dot );
 
        MidiByteArray build_led( const Led & led, LedState ls );
        MidiByteArray build_led( const Button & button, LedState ls );
@@ -61,7 +64,8 @@ public:
        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 );
+       /// And blank the display for the strip. Pass SurfacePort so we know which sysex header to use.
+       MidiByteArray zero_strip( SurfacePort &, const Strip & strip );
        
        // provide bytes to zero the given control
        MidiByteArray zero_control( const Control & control );
@@ -72,6 +76,25 @@ public:
        MidiByteArray two_char_display( const std::string & msg, const std::string & dots = "  " );
        MidiByteArray two_char_display( unsigned int value, const std::string & dots = "  " );
        
+       /**
+               Timecode display. Only the difference between timecode and last_timecode will
+               be encoded, to save midi bandwidth. If they're the same, an empty array will
+               be returned
+       */
+       MidiByteArray timecode_display( SurfacePort &, const std::string & timecode, const std::string & last_timecode = "" );
+       
+       /**
+               for displaying characters on the strip LCD
+               pass SurfacePort so we know which sysex header to use
+       */
+       MidiByteArray strip_display( SurfacePort &, const Strip & strip, unsigned int line_number, const std::string & line );
+       
+       /// blank the strip LCD, ie write all spaces. Pass SurfacePort so we know which sysex header to use.
+       MidiByteArray strip_display_blank( SurfacePort &, const Strip & strip, unsigned int line_number );
+       
+       /// for generating all strip names. Pass SurfacePort so we know which sysex header to use.
+       MidiByteArray all_strips_display( SurfacePort &, std::vector<std::string> & lines1, std::vector<std::string> & lines2 );
+       
 protected:
        static MIDI::byte calculate_pot_value( midi_pot_mode mode, const ControlState & );
 };
index 75e78e7f15fc35797a2d617d2a9e563594508082..d767c4c7450877bb095e63cc9d071707deb4debe 100644 (file)
@@ -23,6 +23,8 @@
 #include "controls.h"
 #include "surface.h"
 
+#include <glibmm/main.h>
+
 #include <midi++/types.h>
 #include <midi++/port.h>
 #include <sigc++/sigc++.h>
@@ -49,14 +51,20 @@ MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int numb
 , _emulation( none )
 , _initialising( true )
 {
-       //cout << "MackiePort::MackiePort" <<endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::MackiePort" <<endl;
+#endif
 }
 
 MackiePort::~MackiePort()
 {
-       //cout << "~MackiePort" << endl;
+#ifdef PORT_DEBUG
+       cout << "~MackiePort" << endl;
+#endif
        close();
-       //cout << "~MackiePort finished" << endl;
+#ifdef PORT_DEBUG
+       cout << "~MackiePort finished" << endl;
+#endif
 }
 
 int MackiePort::strips() const
@@ -83,7 +91,9 @@ int MackiePort::strips() const
 // should really be in MackiePort
 void MackiePort::open()
 {
-       //cout << "MackiePort::open " << *this << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::open " << *this << endl;
+#endif
        _sysex = port().input()->sysex.connect( ( mem_fun (*this, &MackiePort::handle_midi_sysex) ) );
        
        // make sure the device is connected
@@ -92,14 +102,18 @@ void MackiePort::open()
 
 void MackiePort::close()
 {
-       //cout << "MackiePort::close" << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::close" << endl;
+#endif
        
        // disconnect signals
        _any.disconnect();
        _sysex.disconnect();
        
        // TODO emit a "closing" signal?
-       //cout << "MackiePort::close finished" << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::close finished" << endl;
+#endif
 }
 
 const MidiByteArray & MackiePort::sysex_hdr() const
@@ -113,57 +127,6 @@ const MidiByteArray & MackiePort::sysex_hdr() const
        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;
@@ -186,7 +149,9 @@ MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiB
 MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
 {
        // handle host connection query
-       //cout << "host connection query: " << bytes << endl;
+#ifdef PORT_DEBUG
+       cout << "host connection query: " << bytes << endl;
+#endif
        
        if ( bytes.size() != 18 )
        {
@@ -207,7 +172,9 @@ MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
 // not used right now
 MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes )
 {
-       //cout << "host_connection_confirmation: " << bytes << endl;
+#ifdef PORT_DEBUG
+       cout << "host_connection_confirmation: " << bytes << endl;
+#endif
        
        // decode host connection confirmation
        if ( bytes.size() != 14 )
@@ -224,17 +191,20 @@ MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & by
 
 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;
+#if 0
+       cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl;
+
+       MidiByteArray version_string;
+       for ( int i = 6; i < 11; ++i ) version_string << bytes[i];
+       cout << "version_string: " << version_string << endl;
+#endif
        
        // 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;
+               //cout << "MackiePort::probe_emulation out of sequence." << endl;
                return;
        }
 
@@ -243,11 +213,15 @@ void MackiePort::probe_emulation( const MidiByteArray & bytes )
 
 void MackiePort::init()
 {
-       //cout << "MackiePort::init" << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::init" << endl;
+#endif
        init_mutex.lock();
        _initialising = true;
        
-       //cout << "MackiePort::lock acquired" << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::init lock acquired" << endl;
+#endif
        // emit pre-init signal
        init_event();
        
@@ -263,13 +237,19 @@ void MackiePort::init()
 
 void MackiePort::finalise_init( bool yn )
 {
-       //cout << "MackiePort::finalise_init" << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::finalise_init" << endl;
+#endif
        bool emulation_ok = false;
        
        // probing doesn't work very well, so just use a config variable
        // to set the emulation mode
+       // TODO This might have to be specified on a per-port basis
+       // in the config file
+       // if an mcu and a bcf are needed to work as one surface
        if ( _emulation == none )
        {
+               // TODO same as code in mackie_control_protocol.cc
                if ( ARDOUR::Config->get_mackie_emulation() == "bcf" )
                {
                        _emulation = bcf2000;
@@ -296,11 +276,39 @@ void MackiePort::finalise_init( bool yn )
                active_event();
                
                // start handling messages from controls
-               _any = port().input()->any.connect( ( mem_fun (*this, &MackiePort::handle_midi_any) ) );
+               connect_any();
        }
        _initialising = false;
        init_cond.signal();
        init_mutex.unlock();
+#ifdef PORT_DEBUG
+       cout << "MackiePort::finalise_init lock released" << endl;
+#endif
+}
+
+void MackiePort::connect_any()
+{
+/*
+       Doesn't work because there isn't an == operator for slots
+       MIDI::Signal::slot_list_type slots = port().input()->any.slots();
+       
+       if ( find( slots.begin(), slots.end(), mem_fun( *this, &MackiePort::handle_midi_any ) ) == slots.end() )
+*/
+       // TODO but this will break if midi tracing is turned on
+       if ( port().input()->any.empty() )
+       {
+#ifdef DEBUG
+               cout << "connect input parser " << port().input() << " to handle_midi_any" << endl;
+#endif
+               _any = port().input()->any.connect( mem_fun( *this, &MackiePort::handle_midi_any ) );
+#ifdef DEBUG
+               cout << "input parser any connections: " << port().input()->any.size() << endl;
+#endif
+       }
+       else
+       {
+               cout << "MackiePort::connect_any already connected" << endl;
+       }
 }
 
 bool MackiePort::wait_for_init()
@@ -308,18 +316,26 @@ bool MackiePort::wait_for_init()
        Glib::Mutex::Lock lock( init_mutex );
        while ( _initialising )
        {
-               //cout << "MackiePort::wait_for_active waiting" << endl;
+#ifdef PORT_DEBUG
+               cout << "MackiePort::wait_for_active waiting" << endl;
+#endif
                init_cond.wait( init_mutex );
-               //cout << "MackiePort::wait_for_active released" << endl;
+#ifdef PORT_DEBUG
+               cout << "MackiePort::wait_for_active released" << endl;
+#endif
        }
-       //cout << "MackiePort::wait_for_active returning" << endl;
+#ifdef PORT_DEBUG
+       cout << "MackiePort::wait_for_active returning" << endl;
+#endif
        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;
+#ifdef PORT_DEBUG
+       cout << "handle_midi_sysex: " << bytes << endl;
+#endif
        switch( bytes[5] )
        {
                case 0x01:
@@ -342,16 +358,99 @@ void MackiePort::handle_midi_sysex (MIDI::Parser & parser, MIDI::byte * raw_byte
        }
 }
 
+Control & MackiePort::lookup_control( MIDI::byte * bytes, size_t count )
+{
+       // Don't instantiate a MidiByteArray here unless it's needed for exceptions.
+       // Reason being that this method is called for every single incoming
+       // midi event, and it needs to be as efficient as possible.
+       Control * control = 0;
+       MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
+       switch( midi_type )
+       {
+               // fader
+               case MackieMidiBuilder::midi_fader_id:
+               {
+                       int midi_id = bytes[0] & 0x0f;
+                       control = _mcp.surface().faders[midi_id];
+                       if ( control == 0 )
+                       {
+                               MidiByteArray mba( count, bytes );
+                               ostringstream os;
+                               os << "control for fader" << bytes << " id " << midi_id << " is null";
+                               throw MackieControlException( os.str() );
+                       }
+                       break;
+               }
+                       
+               // button
+               case MackieMidiBuilder::midi_button_id:
+                       control = _mcp.surface().buttons[bytes[1]];
+                       if ( control == 0 )
+                       {
+                               MidiByteArray mba( count, bytes );
+                               ostringstream os;
+                               os << "control for button " << bytes << " is null";
+                               throw MackieControlException( os.str() );
+                       }
+                       break;
+                       
+               // pot (jog wheel, external control)
+               case MackieMidiBuilder::midi_pot_id:
+                       control = _mcp.surface().pots[bytes[1]];
+                       if ( control == 0 )
+                       {
+                               MidiByteArray mba( count, bytes );
+                               ostringstream os;
+                               os << "control for rotary " << mba << " is null";
+                               throw MackieControlException( os.str() );
+                       }
+                       break;
+               
+               default:
+                       MidiByteArray mba( count, bytes );
+                       ostringstream os;
+                       os << "Cannot find control for " << bytes;
+                       throw MackieControlException( os.str() );
+       }
+       return *control;
+}
+
+bool MackiePort::handle_control_timeout_event ( Control * control )
+{
+       // empty control_state
+       ControlState control_state;
+       control->in_use( false );
+       control_event( *this, *control, control_state );
+       
+       // only call this method once from the timer
+       return false;
+}
+
 // converts midi messages into control_event signals
+// it might be worth combining this with lookup_control
+// because they have similar logic flows.
 void MackiePort::handle_midi_any (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
 {
+#ifdef DEBUG
        MidiByteArray bytes( count, raw_bytes );
+       cout << "MackiePort::handle_midi_any " << bytes << endl;
+#endif
        try
        {
                // ignore sysex messages
-               if ( bytes[0] == MIDI::sysex ) return;
+               if ( raw_bytes[0] == MIDI::sysex ) return;
 
-               Control & control = lookup_control( bytes );
+               // sanity checking
+               if ( count != 3 )
+               {
+                       ostringstream os;
+                       MidiByteArray mba( count, raw_bytes );
+                       os << "MackiePort::handle_midi_any needs 3 bytes, but received " << mba;
+                       throw MackieControlException( os.str() );
+               }
+               
+               Control & control = lookup_control( raw_bytes, count );
+               control.in_use( true );
                
                // This handles incoming bytes. Outgoing bytes
                // are sent by the signal handlers.
@@ -359,41 +458,74 @@ void MackiePort::handle_midi_any (MIDI::Parser & parser, MIDI::byte * raw_bytes,
                {
                        // 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;
+                       {
+                               // only the top-order 10 bits out of 14 are used
+                               int midi_pos = ( ( raw_bytes[2] << 7 ) + raw_bytes[1] ) >> 4;
+                               
+                               // in_use is set by the MackieControlProtocol::handle_strip_button
+                               
+                               // relies on implicit ControlState constructor
+                               control_event( *this, control, float(midi_pos) / float(0x3ff) );
+                       }
+                       break;
                                
                        // button
                        case Control::type_button:
-                               control_event( *this, control, bytes[2] == 0x7f ? press : release );
+                       {
+                               ControlState control_state( raw_bytes[2] == 0x7f ? press : release );
+                               control.in_use( control_state.button_state == press );
+                               control_event( *this, control, control_state );
+                               
                                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 );
-                               }
+                       {
+                               ControlState state;
+                               
+                               // bytes[2] & 0b01000000 (0x40) give sign
+                               state.sign = ( raw_bytes[2] & 0x40 ) == 0 ? 1 : -1; 
+                               // bytes[2] & 0b00111111 (0x3f) gives delta
+                               state.ticks = ( raw_bytes[2] & 0x3f);
+                               state.delta = float( state.ticks ) / float( 0x3f );
+                               
+                               /*
+                                       Pots only emit events when they move, not when they
+                                       stop moving. So to get a stop event, we need to use a timeout.
+                               */
+                               // this is set to false ...
+                               control.in_use( true );
+                               
+                               // ... by this timeout
+                               
+                               // first disconnect any previous timeouts
+                               control.in_use_connection.disconnect();
+                               
+                               // now connect a new timeout to call handle_control_timeout_event
+                               sigc::slot<bool> timeout_slot = sigc::bind(
+                                       mem_fun( *this, &MackiePort::handle_control_timeout_event )
+                                       , &control
+                               );
+                               control.in_use_connection = Glib::signal_timeout().connect(
+                                       timeout_slot
+                                       , control.in_use_timeout()
+                               );
+                               
+                               // emit the control event
+                               control_event( *this, control, state );
                                break;
+                       }
                        default:
                                cerr << "Do not understand control type " << control;
                }
        }
        catch( MackieControlException & e )
        {
-               //cout << bytes << ' ' << e.what() << endl;
+               MidiByteArray bytes( count, raw_bytes );
+               cout << bytes << ' ' << e.what() << endl;
        }
+#ifdef DEBUG
+       cout << "finished MackiePort::handle_midi_any " << bytes << endl;
+#endif
 }
index 2ad5cf61548e10056ae389567525c3e260419f72..26ec1ca799cc6e4d5701d4fc5484cf35bfdb26a6 100644 (file)
@@ -55,12 +55,12 @@ public:
        virtual const MidiByteArray & sysex_hdr() const;
 
        /// Handle device initialisation
-       void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t );
+       void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t count );
 
        /// Handle all control messags
-       void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t );
+       void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t count );
        
-       Control & lookup_control( const MidiByteArray & bytes );
+       Control & lookup_control( MIDI::byte *, size_t count );
        
        /// return the number of strips associated with this port
        virtual int strips() const;
@@ -71,6 +71,10 @@ public:
        
        emulation_t emulation() const { return _emulation; }
        
+       /// Connect the any signal from the parser to handle_midi_any
+       /// unless it's already connected
+       void connect_any();
+       
 protected:
        /**
                The initialisation sequence is fairly complex. First a lock is acquired
@@ -105,6 +109,10 @@ protected:
        */
        void probe_emulation( const MidiByteArray & bytes );
 
+       /// Handle timeout events set for controls that don't emit
+       /// an off event
+       bool handle_control_timeout_event ( Control * );
+
 private:
        MackieControlProtocol & _mcp;
        port_type_t _port_type;
index b527f710cc4b63e53c61f54850f64e154a130241..dbba726062061a95fb51c0cdecfb85c70b4583f0 100644 (file)
-/*
-       Generated by scripts/generate-surface.rb
-*/
-
 #include "mackie_surface.h"
+#include "surface_port.h"
+#include "mackie_midi_builder.h"
 
-#include "controls.h"
-#include "mackie_button_handler.h"
+#include <cmath>
+#include <string>
 
 using namespace Mackie;
 
-void Mackie::MackieSurface::init_controls()
+void MackieSurface::display_timecode( SurfacePort & port, MackieMidiBuilder & builder, const std::string & timecode, const std::string & timecode_last )
 {
-       // intialise groups and strips
-       Group * group = 0;
-       
-       // make sure there are enough strips
-       strips.resize( 8 );
-       
-       group = new Group ( "user" );
-       groups["user"] = group;
-       
-       group = new Group ( "assignment" );
-       groups["assignment"] = group;
-       
-       group = new Group ( "none" );
-       groups["none"] = group;
-       
-       group = new MasterStrip ( "master", 0 );
-       groups["master"] = group;
-       strips[0] = dynamic_cast<Strip*>( group );
-       
-       group = new Strip ( "strip_1", 0 );
-       groups["strip_1"] = group;
-       strips[0] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "cursor" );
-       groups["cursor"] = group;
-       
-       group = new Strip ( "strip_2", 1 );
-       groups["strip_2"] = group;
-       strips[1] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "functions" );
-       groups["functions"] = group;
-       
-       group = new Group ( "automation" );
-       groups["automation"] = group;
-       
-       group = new Strip ( "strip_3", 2 );
-       groups["strip_3"] = group;
-       strips[2] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "display" );
-       groups["display"] = group;
-       
-       group = new Strip ( "strip_4", 3 );
-       groups["strip_4"] = group;
-       strips[3] = dynamic_cast<Strip*>( group );
-       
-       group = new Strip ( "strip_5", 4 );
-       groups["strip_5"] = group;
-       strips[4] = dynamic_cast<Strip*>( group );
-       
-       group = new Strip ( "strip_6", 5 );
-       groups["strip_6"] = group;
-       strips[5] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "transport" );
-       groups["transport"] = group;
-       
-       group = new Strip ( "strip_7", 6 );
-       groups["strip_7"] = group;
-       strips[6] = dynamic_cast<Strip*>( group );
-       
-       group = new Group ( "modifiers" );
-       groups["modifiers"] = group;
-       
-       group = new Group ( "bank" );
-       groups["bank"] = group;
-       
-       group = new Strip ( "strip_8", 7 );
-       groups["strip_8"] = group;
-       strips[7] = dynamic_cast<Strip*>( group );
-       
-
-       // initialise controls
-       Control * control = 0;
-
-       group = groups["strip_1"];
-       control = new Fader ( 0, 1, "gain", *group );
-       faders[0x00] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Fader ( 1, 2, "gain", *group );
-       faders[0x01] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Fader ( 2, 3, "gain", *group );
-       faders[0x02] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Fader ( 3, 4, "gain", *group );
-       faders[0x03] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Fader ( 4, 5, "gain", *group );
-       faders[0x04] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Fader ( 5, 6, "gain", *group );
-       faders[0x05] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Fader ( 6, 7, "gain", *group );
-       faders[0x06] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Fader ( 7, 8, "gain", *group );
-       faders[0x07] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["master"];
-       control = new Fader ( 8, 1, "gain", *group );
-       faders[0x08] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Pot ( 16, 1, "vpot", *group );
-       pots[0x10] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Pot ( 17, 2, "vpot", *group );
-       pots[0x11] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Pot ( 18, 3, "vpot", *group );
-       pots[0x12] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Pot ( 19, 4, "vpot", *group );
-       pots[0x13] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Pot ( 20, 5, "vpot", *group );
-       pots[0x14] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Pot ( 21, 6, "vpot", *group );
-       pots[0x15] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Pot ( 22, 7, "vpot", *group );
-       pots[0x16] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Pot ( 23, 8, "vpot", *group );
-       pots[0x17] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Pot ( 60, 1, "jog", *group );
-       pots[0x3c] = control;
-       controls.push_back( control );
-       controls_by_name["jog"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Pot ( 46, 1, "external", *group );
-       pots[0x2e] = control;
-       controls.push_back( control );
-       controls_by_name["external"] = control;
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 0, 1, "recenable", *group );
-       buttons[0x00] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 1, 2, "recenable", *group );
-       buttons[0x01] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 2, 3, "recenable", *group );
-       buttons[0x02] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 3, 4, "recenable", *group );
-       buttons[0x03] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 4, 5, "recenable", *group );
-       buttons[0x04] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 5, 6, "recenable", *group );
-       buttons[0x05] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 6, 7, "recenable", *group );
-       buttons[0x06] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Button ( 7, 8, "recenable", *group );
-       buttons[0x07] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 8, 1, "solo", *group );
-       buttons[0x08] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 9, 2, "solo", *group );
-       buttons[0x09] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 10, 3, "solo", *group );
-       buttons[0x0a] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 11, 4, "solo", *group );
-       buttons[0x0b] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 12, 5, "solo", *group );
-       buttons[0x0c] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 13, 6, "solo", *group );
-       buttons[0x0d] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 14, 7, "solo", *group );
-       buttons[0x0e] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Button ( 15, 8, "solo", *group );
-       buttons[0x0f] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 16, 1, "mute", *group );
-       buttons[0x10] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 17, 2, "mute", *group );
-       buttons[0x11] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 18, 3, "mute", *group );
-       buttons[0x12] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 19, 4, "mute", *group );
-       buttons[0x13] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 20, 5, "mute", *group );
-       buttons[0x14] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 21, 6, "mute", *group );
-       buttons[0x15] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 22, 7, "mute", *group );
-       buttons[0x16] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Button ( 23, 8, "mute", *group );
-       buttons[0x17] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 24, 1, "select", *group );
-       buttons[0x18] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 25, 2, "select", *group );
-       buttons[0x19] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 26, 3, "select", *group );
-       buttons[0x1a] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 27, 4, "select", *group );
-       buttons[0x1b] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 28, 5, "select", *group );
-       buttons[0x1c] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 29, 6, "select", *group );
-       buttons[0x1d] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 30, 7, "select", *group );
-       buttons[0x1e] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Button ( 31, 8, "select", *group );
-       buttons[0x1f] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 32, 1, "vselect", *group );
-       buttons[0x20] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 33, 2, "vselect", *group );
-       buttons[0x21] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 34, 3, "vselect", *group );
-       buttons[0x22] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 35, 4, "vselect", *group );
-       buttons[0x23] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 36, 5, "vselect", *group );
-       buttons[0x24] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 37, 6, "vselect", *group );
-       buttons[0x25] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 38, 7, "vselect", *group );
-       buttons[0x26] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Button ( 39, 8, "vselect", *group );
-       buttons[0x27] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 40, 1, "io", *group );
-       buttons[0x28] = control;
-       controls.push_back( control );
-       controls_by_name["io"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 41, 1, "sends", *group );
-       buttons[0x29] = control;
-       controls.push_back( control );
-       controls_by_name["sends"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 42, 1, "pan", *group );
-       buttons[0x2a] = control;
-       controls.push_back( control );
-       controls_by_name["pan"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 43, 1, "plugin", *group );
-       buttons[0x2b] = control;
-       controls.push_back( control );
-       controls_by_name["plugin"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 44, 1, "eq", *group );
-       buttons[0x2c] = control;
-       controls.push_back( control );
-       controls_by_name["eq"] = control;
-       group->add( *control );
-
-       group = groups["assignment"];
-       control = new Button ( 45, 1, "dyn", *group );
-       buttons[0x2d] = control;
-       controls.push_back( control );
-       controls_by_name["dyn"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 46, 1, "left", *group );
-       buttons[0x2e] = control;
-       controls.push_back( control );
-       controls_by_name["left"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 47, 1, "right", *group );
-       buttons[0x2f] = control;
-       controls.push_back( control );
-       controls_by_name["right"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 48, 1, "channel_left", *group );
-       buttons[0x30] = control;
-       controls.push_back( control );
-       controls_by_name["channel_left"] = control;
-       group->add( *control );
-
-       group = groups["bank"];
-       control = new Button ( 49, 1, "channel_right", *group );
-       buttons[0x31] = control;
-       controls.push_back( control );
-       controls_by_name["channel_right"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 50, 1, "flip", *group );
-       buttons[0x32] = control;
-       controls.push_back( control );
-       controls_by_name["flip"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 51, 1, "edit", *group );
-       buttons[0x33] = control;
-       controls.push_back( control );
-       controls_by_name["edit"] = control;
-       group->add( *control );
-
-       group = groups["display"];
-       control = new Button ( 52, 1, "name_value", *group );
-       buttons[0x34] = control;
-       controls.push_back( control );
-       controls_by_name["name_value"] = control;
-       group->add( *control );
-
-       group = groups["display"];
-       control = new Button ( 53, 1, "smpte_beats", *group );
-       buttons[0x35] = control;
-       controls.push_back( control );
-       controls_by_name["smpte_beats"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 54, 1, "F1", *group );
-       buttons[0x36] = control;
-       controls.push_back( control );
-       controls_by_name["F1"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 55, 1, "F2", *group );
-       buttons[0x37] = control;
-       controls.push_back( control );
-       controls_by_name["F2"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 56, 1, "F3", *group );
-       buttons[0x38] = control;
-       controls.push_back( control );
-       controls_by_name["F3"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 57, 1, "F4", *group );
-       buttons[0x39] = control;
-       controls.push_back( control );
-       controls_by_name["F4"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 58, 1, "F5", *group );
-       buttons[0x3a] = control;
-       controls.push_back( control );
-       controls_by_name["F5"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 59, 1, "F6", *group );
-       buttons[0x3b] = control;
-       controls.push_back( control );
-       controls_by_name["F6"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 60, 1, "F7", *group );
-       buttons[0x3c] = control;
-       controls.push_back( control );
-       controls_by_name["F7"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 61, 1, "F8", *group );
-       buttons[0x3d] = control;
-       controls.push_back( control );
-       controls_by_name["F8"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 62, 1, "F9", *group );
-       buttons[0x3e] = control;
-       controls.push_back( control );
-       controls_by_name["F9"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 63, 1, "F10", *group );
-       buttons[0x3f] = control;
-       controls.push_back( control );
-       controls_by_name["F10"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 64, 1, "F11", *group );
-       buttons[0x40] = control;
-       controls.push_back( control );
-       controls_by_name["F11"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 65, 1, "F12", *group );
-       buttons[0x41] = control;
-       controls.push_back( control );
-       controls_by_name["F12"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 66, 1, "F13", *group );
-       buttons[0x42] = control;
-       controls.push_back( control );
-       controls_by_name["F13"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 67, 1, "F14", *group );
-       buttons[0x43] = control;
-       controls.push_back( control );
-       controls_by_name["F14"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 68, 1, "F15", *group );
-       buttons[0x44] = control;
-       controls.push_back( control );
-       controls_by_name["F15"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 69, 1, "F16", *group );
-       buttons[0x45] = control;
-       controls.push_back( control );
-       controls_by_name["F16"] = control;
-       group->add( *control );
-
-       group = groups["modifiers"];
-       control = new Button ( 70, 1, "shift", *group );
-       buttons[0x46] = control;
-       controls.push_back( control );
-       controls_by_name["shift"] = control;
-       group->add( *control );
-
-       group = groups["modifiers"];
-       control = new Button ( 71, 1, "option", *group );
-       buttons[0x47] = control;
-       controls.push_back( control );
-       controls_by_name["option"] = control;
-       group->add( *control );
-
-       group = groups["modifiers"];
-       control = new Button ( 72, 1, "control", *group );
-       buttons[0x48] = control;
-       controls.push_back( control );
-       controls_by_name["control"] = control;
-       group->add( *control );
-
-       group = groups["modifiers"];
-       control = new Button ( 73, 1, "cmd_alt", *group );
-       buttons[0x49] = control;
-       controls.push_back( control );
-       controls_by_name["cmd_alt"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 74, 1, "on", *group );
-       buttons[0x4a] = control;
-       controls.push_back( control );
-       controls_by_name["on"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 75, 1, "rec_ready", *group );
-       buttons[0x4b] = control;
-       controls.push_back( control );
-       controls_by_name["rec_ready"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 76, 1, "undo", *group );
-       buttons[0x4c] = control;
-       controls.push_back( control );
-       controls_by_name["undo"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 77, 1, "snapshot", *group );
-       buttons[0x4d] = control;
-       controls.push_back( control );
-       controls_by_name["snapshot"] = control;
-       group->add( *control );
-
-       group = groups["automation"];
-       control = new Button ( 78, 1, "touch", *group );
-       buttons[0x4e] = control;
-       controls.push_back( control );
-       controls_by_name["touch"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 79, 1, "redo", *group );
-       buttons[0x4f] = control;
-       controls.push_back( control );
-       controls_by_name["redo"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 80, 1, "marker", *group );
-       buttons[0x50] = control;
-       controls.push_back( control );
-       controls_by_name["marker"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 81, 1, "enter", *group );
-       buttons[0x51] = control;
-       controls.push_back( control );
-       controls_by_name["enter"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 82, 1, "cancel", *group );
-       buttons[0x52] = control;
-       controls.push_back( control );
-       controls_by_name["cancel"] = control;
-       group->add( *control );
-
-       group = groups["functions"];
-       control = new Button ( 83, 1, "mixer", *group );
-       buttons[0x53] = control;
-       controls.push_back( control );
-       controls_by_name["mixer"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 84, 1, "frm_left", *group );
-       buttons[0x54] = control;
-       controls.push_back( control );
-       controls_by_name["frm_left"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 85, 1, "frm_right", *group );
-       buttons[0x55] = control;
-       controls.push_back( control );
-       controls_by_name["frm_right"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 86, 1, "loop", *group );
-       buttons[0x56] = control;
-       controls.push_back( control );
-       controls_by_name["loop"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 87, 1, "punch_in", *group );
-       buttons[0x57] = control;
-       controls.push_back( control );
-       controls_by_name["punch_in"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 88, 1, "punch_out", *group );
-       buttons[0x58] = control;
-       controls.push_back( control );
-       controls_by_name["punch_out"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 89, 1, "home", *group );
-       buttons[0x59] = control;
-       controls.push_back( control );
-       controls_by_name["home"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 90, 1, "end", *group );
-       buttons[0x5a] = control;
-       controls.push_back( control );
-       controls_by_name["end"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 91, 1, "rewind", *group );
-       buttons[0x5b] = control;
-       controls.push_back( control );
-       controls_by_name["rewind"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 92, 1, "ffwd", *group );
-       buttons[0x5c] = control;
-       controls.push_back( control );
-       controls_by_name["ffwd"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 93, 1, "stop", *group );
-       buttons[0x5d] = control;
-       controls.push_back( control );
-       controls_by_name["stop"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 94, 1, "play", *group );
-       buttons[0x5e] = control;
-       controls.push_back( control );
-       controls_by_name["play"] = control;
-       group->add( *control );
-
-       group = groups["transport"];
-       control = new Button ( 95, 1, "record", *group );
-       buttons[0x5f] = control;
-       controls.push_back( control );
-       controls_by_name["record"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 96, 1, "cursor_up", *group );
-       buttons[0x60] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_up"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 97, 1, "cursor_down", *group );
-       buttons[0x61] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_down"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 98, 1, "cursor_left", *group );
-       buttons[0x62] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_left"] = control;
-       group->add( *control );
-
-       group = groups["cursor"];
-       control = new Button ( 99, 1, "cursor_right", *group );
-       buttons[0x63] = control;
-       controls.push_back( control );
-       controls_by_name["cursor_right"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 100, 1, "zoom", *group );
-       buttons[0x64] = control;
-       controls.push_back( control );
-       controls_by_name["zoom"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Button ( 101, 1, "scrub", *group );
-       buttons[0x65] = control;
-       controls.push_back( control );
-       controls_by_name["scrub"] = control;
-       group->add( *control );
-
-       group = groups["user"];
-       control = new Button ( 102, 1, "user_a", *group );
-       buttons[0x66] = control;
-       controls.push_back( control );
-       controls_by_name["user_a"] = control;
-       group->add( *control );
-
-       group = groups["user"];
-       control = new Button ( 103, 1, "user_b", *group );
-       buttons[0x67] = control;
-       controls.push_back( control );
-       controls_by_name["user_b"] = control;
-       group->add( *control );
-
-       group = groups["strip_1"];
-       control = new Button ( 104, 1, "fader_touch", *group );
-       buttons[0x68] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_2"];
-       control = new Button ( 105, 2, "fader_touch", *group );
-       buttons[0x69] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_3"];
-       control = new Button ( 106, 3, "fader_touch", *group );
-       buttons[0x6a] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_4"];
-       control = new Button ( 107, 4, "fader_touch", *group );
-       buttons[0x6b] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_5"];
-       control = new Button ( 108, 5, "fader_touch", *group );
-       buttons[0x6c] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_6"];
-       control = new Button ( 109, 6, "fader_touch", *group );
-       buttons[0x6d] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_7"];
-       control = new Button ( 110, 7, "fader_touch", *group );
-       buttons[0x6e] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["strip_8"];
-       control = new Button ( 111, 8, "fader_touch", *group );
-       buttons[0x6f] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["master"];
-       control = new Button ( 112, 1, "fader_touch", *group );
-       buttons[0x70] = control;
-       controls.push_back( control );
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 113, 1, "smpte", *group );
-       leds[0x71] = control;
-       controls.push_back( control );
-       controls_by_name["smpte"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 114, 1, "beats", *group );
-       leds[0x72] = control;
-       controls.push_back( control );
-       controls_by_name["beats"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 115, 1, "solo", *group );
-       leds[0x73] = control;
-       controls.push_back( control );
-       controls_by_name["solo"] = control;
-       group->add( *control );
-
-       group = groups["none"];
-       control = new Led ( 118, 1, "relay_click", *group );
-       leds[0x76] = control;
-       controls.push_back( control );
-       controls_by_name["relay_click"] = control;
-       group->add( *control );
-
+       port.write( builder.timecode_display( port, timecode, timecode_last ) );
 }
 
-void Mackie::MackieSurface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
+float MackieSurface::scaled_delta( const ControlState & state, float current_speed )
 {
-       if ( bs != press && bs != release )
-       {
-               mbh.update_led( button, none );
-               return;
-       }
-       
-       LedState ls;
-       switch ( button.id() )
-       {
-
-               case 0x28: // io
-                       switch ( bs ) {
-                               case press: ls = mbh.io_press( button ); break;
-                               case release: ls = mbh.io_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x29: // sends
-                       switch ( bs ) {
-                               case press: ls = mbh.sends_press( button ); break;
-                               case release: ls = mbh.sends_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2a: // pan
-                       switch ( bs ) {
-                               case press: ls = mbh.pan_press( button ); break;
-                               case release: ls = mbh.pan_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2b: // plugin
-                       switch ( bs ) {
-                               case press: ls = mbh.plugin_press( button ); break;
-                               case release: ls = mbh.plugin_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2c: // eq
-                       switch ( bs ) {
-                               case press: ls = mbh.eq_press( button ); break;
-                               case release: ls = mbh.eq_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2d: // dyn
-                       switch ( bs ) {
-                               case press: ls = mbh.dyn_press( button ); break;
-                               case release: ls = mbh.dyn_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2e: // left
-                       switch ( bs ) {
-                               case press: ls = mbh.left_press( button ); break;
-                               case release: ls = mbh.left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x2f: // right
-                       switch ( bs ) {
-                               case press: ls = mbh.right_press( button ); break;
-                               case release: ls = mbh.right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x30: // channel_left
-                       switch ( bs ) {
-                               case press: ls = mbh.channel_left_press( button ); break;
-                               case release: ls = mbh.channel_left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x31: // channel_right
-                       switch ( bs ) {
-                               case press: ls = mbh.channel_right_press( button ); break;
-                               case release: ls = mbh.channel_right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x32: // flip
-                       switch ( bs ) {
-                               case press: ls = mbh.flip_press( button ); break;
-                               case release: ls = mbh.flip_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x33: // edit
-                       switch ( bs ) {
-                               case press: ls = mbh.edit_press( button ); break;
-                               case release: ls = mbh.edit_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x34: // name_value
-                       switch ( bs ) {
-                               case press: ls = mbh.name_value_press( button ); break;
-                               case release: ls = mbh.name_value_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x35: // smpte_beats
-                       switch ( bs ) {
-                               case press: ls = mbh.smpte_beats_press( button ); break;
-                               case release: ls = mbh.smpte_beats_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x36: // F1
-                       switch ( bs ) {
-                               case press: ls = mbh.F1_press( button ); break;
-                               case release: ls = mbh.F1_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x37: // F2
-                       switch ( bs ) {
-                               case press: ls = mbh.F2_press( button ); break;
-                               case release: ls = mbh.F2_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x38: // F3
-                       switch ( bs ) {
-                               case press: ls = mbh.F3_press( button ); break;
-                               case release: ls = mbh.F3_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x39: // F4
-                       switch ( bs ) {
-                               case press: ls = mbh.F4_press( button ); break;
-                               case release: ls = mbh.F4_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3a: // F5
-                       switch ( bs ) {
-                               case press: ls = mbh.F5_press( button ); break;
-                               case release: ls = mbh.F5_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3b: // F6
-                       switch ( bs ) {
-                               case press: ls = mbh.F6_press( button ); break;
-                               case release: ls = mbh.F6_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3c: // F7
-                       switch ( bs ) {
-                               case press: ls = mbh.F7_press( button ); break;
-                               case release: ls = mbh.F7_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3d: // F8
-                       switch ( bs ) {
-                               case press: ls = mbh.F8_press( button ); break;
-                               case release: ls = mbh.F8_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3e: // F9
-                       switch ( bs ) {
-                               case press: ls = mbh.F9_press( button ); break;
-                               case release: ls = mbh.F9_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x3f: // F10
-                       switch ( bs ) {
-                               case press: ls = mbh.F10_press( button ); break;
-                               case release: ls = mbh.F10_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x40: // F11
-                       switch ( bs ) {
-                               case press: ls = mbh.F11_press( button ); break;
-                               case release: ls = mbh.F11_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x41: // F12
-                       switch ( bs ) {
-                               case press: ls = mbh.F12_press( button ); break;
-                               case release: ls = mbh.F12_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x42: // F13
-                       switch ( bs ) {
-                               case press: ls = mbh.F13_press( button ); break;
-                               case release: ls = mbh.F13_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x43: // F14
-                       switch ( bs ) {
-                               case press: ls = mbh.F14_press( button ); break;
-                               case release: ls = mbh.F14_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x44: // F15
-                       switch ( bs ) {
-                               case press: ls = mbh.F15_press( button ); break;
-                               case release: ls = mbh.F15_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x45: // F16
-                       switch ( bs ) {
-                               case press: ls = mbh.F16_press( button ); break;
-                               case release: ls = mbh.F16_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x46: // shift
-                       switch ( bs ) {
-                               case press: ls = mbh.shift_press( button ); break;
-                               case release: ls = mbh.shift_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x47: // option
-                       switch ( bs ) {
-                               case press: ls = mbh.option_press( button ); break;
-                               case release: ls = mbh.option_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x48: // control
-                       switch ( bs ) {
-                               case press: ls = mbh.control_press( button ); break;
-                               case release: ls = mbh.control_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x49: // cmd_alt
-                       switch ( bs ) {
-                               case press: ls = mbh.cmd_alt_press( button ); break;
-                               case release: ls = mbh.cmd_alt_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4a: // on
-                       switch ( bs ) {
-                               case press: ls = mbh.on_press( button ); break;
-                               case release: ls = mbh.on_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4b: // rec_ready
-                       switch ( bs ) {
-                               case press: ls = mbh.rec_ready_press( button ); break;
-                               case release: ls = mbh.rec_ready_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4c: // undo
-                       switch ( bs ) {
-                               case press: ls = mbh.undo_press( button ); break;
-                               case release: ls = mbh.undo_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4d: // snapshot
-                       switch ( bs ) {
-                               case press: ls = mbh.snapshot_press( button ); break;
-                               case release: ls = mbh.snapshot_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4e: // touch
-                       switch ( bs ) {
-                               case press: ls = mbh.touch_press( button ); break;
-                               case release: ls = mbh.touch_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x4f: // redo
-                       switch ( bs ) {
-                               case press: ls = mbh.redo_press( button ); break;
-                               case release: ls = mbh.redo_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x50: // marker
-                       switch ( bs ) {
-                               case press: ls = mbh.marker_press( button ); break;
-                               case release: ls = mbh.marker_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x51: // enter
-                       switch ( bs ) {
-                               case press: ls = mbh.enter_press( button ); break;
-                               case release: ls = mbh.enter_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x52: // cancel
-                       switch ( bs ) {
-                               case press: ls = mbh.cancel_press( button ); break;
-                               case release: ls = mbh.cancel_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x53: // mixer
-                       switch ( bs ) {
-                               case press: ls = mbh.mixer_press( button ); break;
-                               case release: ls = mbh.mixer_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x54: // frm_left
-                       switch ( bs ) {
-                               case press: ls = mbh.frm_left_press( button ); break;
-                               case release: ls = mbh.frm_left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x55: // frm_right
-                       switch ( bs ) {
-                               case press: ls = mbh.frm_right_press( button ); break;
-                               case release: ls = mbh.frm_right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x56: // loop
-                       switch ( bs ) {
-                               case press: ls = mbh.loop_press( button ); break;
-                               case release: ls = mbh.loop_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x57: // punch_in
-                       switch ( bs ) {
-                               case press: ls = mbh.punch_in_press( button ); break;
-                               case release: ls = mbh.punch_in_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x58: // punch_out
-                       switch ( bs ) {
-                               case press: ls = mbh.punch_out_press( button ); break;
-                               case release: ls = mbh.punch_out_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x59: // home
-                       switch ( bs ) {
-                               case press: ls = mbh.home_press( button ); break;
-                               case release: ls = mbh.home_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5a: // end
-                       switch ( bs ) {
-                               case press: ls = mbh.end_press( button ); break;
-                               case release: ls = mbh.end_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5b: // rewind
-                       switch ( bs ) {
-                               case press: ls = mbh.rewind_press( button ); break;
-                               case release: ls = mbh.rewind_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5c: // ffwd
-                       switch ( bs ) {
-                               case press: ls = mbh.ffwd_press( button ); break;
-                               case release: ls = mbh.ffwd_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5d: // stop
-                       switch ( bs ) {
-                               case press: ls = mbh.stop_press( button ); break;
-                               case release: ls = mbh.stop_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5e: // play
-                       switch ( bs ) {
-                               case press: ls = mbh.play_press( button ); break;
-                               case release: ls = mbh.play_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x5f: // record
-                       switch ( bs ) {
-                               case press: ls = mbh.record_press( button ); break;
-                               case release: ls = mbh.record_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x60: // cursor_up
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_up_press( button ); break;
-                               case release: ls = mbh.cursor_up_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x61: // cursor_down
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_down_press( button ); break;
-                               case release: ls = mbh.cursor_down_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x62: // cursor_left
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_left_press( button ); break;
-                               case release: ls = mbh.cursor_left_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x63: // cursor_right
-                       switch ( bs ) {
-                               case press: ls = mbh.cursor_right_press( button ); break;
-                               case release: ls = mbh.cursor_right_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x64: // zoom
-                       switch ( bs ) {
-                               case press: ls = mbh.zoom_press( button ); break;
-                               case release: ls = mbh.zoom_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x65: // scrub
-                       switch ( bs ) {
-                               case press: ls = mbh.scrub_press( button ); break;
-                               case release: ls = mbh.scrub_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x66: // user_a
-                       switch ( bs ) {
-                               case press: ls = mbh.user_a_press( button ); break;
-                               case release: ls = mbh.user_a_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-               case 0x67: // user_b
-                       switch ( bs ) {
-                               case press: ls = mbh.user_b_press( button ); break;
-                               case release: ls = mbh.user_b_release( button ); break;
-                               case neither: break;
-                       }
-                       break;
-
-       }
-       mbh.update_led( button, ls );
+       return state.sign * ( std::pow( float(state.ticks + 1), 2 ) + current_speed ) / 100.0;
 }
index 735cbc5851feb68bfb1d4c931476162ea0f4be2e..b3bfb3b6d4c42cab6c38e1547a223dd4144585bc 100644 (file)
@@ -10,7 +10,6 @@ namespace Mackie
 {
 
 class MackieButtonHandler;
-
 class MackieSurface : public Surface
 {
 public:
@@ -20,6 +19,12 @@ public:
        
        virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
        virtual void init_controls();
+
+       virtual bool has_timecode_display() const { return true; }
+       virtual void display_timecode( SurfacePort &, MackieMidiBuilder &, const std::string & timecode, const std::string & timecode_last );
+
+       virtual float scrub_scaling_factor() { return 100.0; }
+       virtual float scaled_delta( const ControlState & state, float current_speed );
 };
 
 }
diff --git a/libs/surfaces/mackie/mackie_surface_generated.cc b/libs/surfaces/mackie/mackie_surface_generated.cc
new file mode 100644 (file)
index 0000000..f284a05
--- /dev/null
@@ -0,0 +1,1507 @@
+/*
+       Generated by scripts/generate-surface.rb
+*/
+
+#include "mackie_surface.h"
+
+#include "controls.h"
+#include "mackie_button_handler.h"
+
+using namespace Mackie;
+
+void Mackie::MackieSurface::init_controls()
+{
+       // intialise groups and strips
+       Group * group = 0;
+       
+       // make sure there are enough strips
+       strips.resize( 8 );
+       
+       group = new Group ( "user" );
+       groups["user"] = group;
+       
+       group = new Group ( "assignment" );
+       groups["assignment"] = group;
+       
+       group = new Group ( "none" );
+       groups["none"] = group;
+       
+       group = new MasterStrip ( "master", 0 );
+       groups["master"] = group;
+       strips[0] = dynamic_cast<Strip*>( group );
+       
+       group = new Strip ( "strip_1", 0 );
+       groups["strip_1"] = group;
+       strips[0] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "cursor" );
+       groups["cursor"] = group;
+       
+       group = new Strip ( "strip_2", 1 );
+       groups["strip_2"] = group;
+       strips[1] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "functions" );
+       groups["functions"] = group;
+       
+       group = new Group ( "automation" );
+       groups["automation"] = group;
+       
+       group = new Strip ( "strip_3", 2 );
+       groups["strip_3"] = group;
+       strips[2] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "display" );
+       groups["display"] = group;
+       
+       group = new Strip ( "strip_4", 3 );
+       groups["strip_4"] = group;
+       strips[3] = dynamic_cast<Strip*>( group );
+       
+       group = new Strip ( "strip_5", 4 );
+       groups["strip_5"] = group;
+       strips[4] = dynamic_cast<Strip*>( group );
+       
+       group = new Strip ( "strip_6", 5 );
+       groups["strip_6"] = group;
+       strips[5] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "transport" );
+       groups["transport"] = group;
+       
+       group = new Strip ( "strip_7", 6 );
+       groups["strip_7"] = group;
+       strips[6] = dynamic_cast<Strip*>( group );
+       
+       group = new Group ( "modifiers" );
+       groups["modifiers"] = group;
+       
+       group = new Group ( "bank" );
+       groups["bank"] = group;
+       
+       group = new Strip ( "strip_8", 7 );
+       groups["strip_8"] = group;
+       strips[7] = dynamic_cast<Strip*>( group );
+       
+
+       // initialise controls
+       Fader * fader = 0;
+       Pot * pot = 0;
+       Button * button = 0;
+       Led * led = 0;
+
+       group = groups["strip_1"];
+       fader = new Fader ( 0, 1, "gain", *group );
+       faders[0x00] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_2"];
+       fader = new Fader ( 1, 2, "gain", *group );
+       faders[0x01] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_3"];
+       fader = new Fader ( 2, 3, "gain", *group );
+       faders[0x02] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_4"];
+       fader = new Fader ( 3, 4, "gain", *group );
+       faders[0x03] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_5"];
+       fader = new Fader ( 4, 5, "gain", *group );
+       faders[0x04] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_6"];
+       fader = new Fader ( 5, 6, "gain", *group );
+       faders[0x05] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_7"];
+       fader = new Fader ( 6, 7, "gain", *group );
+       faders[0x06] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_8"];
+       fader = new Fader ( 7, 8, "gain", *group );
+       faders[0x07] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["master"];
+       fader = new Fader ( 8, 1, "gain", *group );
+       faders[0x08] = fader;
+       controls.push_back( fader );
+       group->add( *fader );
+
+       group = groups["strip_1"];
+       pot = new Pot ( 16, 1, "vpot", *group );
+       pots[0x10] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_2"];
+       pot = new Pot ( 17, 2, "vpot", *group );
+       pots[0x11] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_3"];
+       pot = new Pot ( 18, 3, "vpot", *group );
+       pots[0x12] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_4"];
+       pot = new Pot ( 19, 4, "vpot", *group );
+       pots[0x13] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_5"];
+       pot = new Pot ( 20, 5, "vpot", *group );
+       pots[0x14] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_6"];
+       pot = new Pot ( 21, 6, "vpot", *group );
+       pots[0x15] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_7"];
+       pot = new Pot ( 22, 7, "vpot", *group );
+       pots[0x16] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["strip_8"];
+       pot = new Pot ( 23, 8, "vpot", *group );
+       pots[0x17] = pot;
+       controls.push_back( pot );
+       group->add( *pot );
+
+       group = groups["none"];
+       pot = new Jog ( 60, 1, "jog", *group );
+       pots[0x3c] = pot;
+       controls.push_back( pot );
+       controls_by_name["jog"] = pot;
+       group->add( *pot );
+
+       group = groups["none"];
+       pot = new Pot ( 46, 1, "external", *group );
+       pots[0x2e] = pot;
+       controls.push_back( pot );
+       controls_by_name["external"] = pot;
+       group->add( *pot );
+
+       group = groups["strip_1"];
+       button = new Button ( 0, 1, "recenable", *group );
+       buttons[0x00] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 1, 2, "recenable", *group );
+       buttons[0x01] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 2, 3, "recenable", *group );
+       buttons[0x02] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 3, 4, "recenable", *group );
+       buttons[0x03] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 4, 5, "recenable", *group );
+       buttons[0x04] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 5, 6, "recenable", *group );
+       buttons[0x05] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 6, 7, "recenable", *group );
+       buttons[0x06] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_8"];
+       button = new Button ( 7, 8, "recenable", *group );
+       buttons[0x07] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 8, 1, "solo", *group );
+       buttons[0x08] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 9, 2, "solo", *group );
+       buttons[0x09] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 10, 3, "solo", *group );
+       buttons[0x0a] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 11, 4, "solo", *group );
+       buttons[0x0b] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 12, 5, "solo", *group );
+       buttons[0x0c] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 13, 6, "solo", *group );
+       buttons[0x0d] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 14, 7, "solo", *group );
+       buttons[0x0e] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_8"];
+       button = new Button ( 15, 8, "solo", *group );
+       buttons[0x0f] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 16, 1, "mute", *group );
+       buttons[0x10] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 17, 2, "mute", *group );
+       buttons[0x11] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 18, 3, "mute", *group );
+       buttons[0x12] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 19, 4, "mute", *group );
+       buttons[0x13] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 20, 5, "mute", *group );
+       buttons[0x14] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 21, 6, "mute", *group );
+       buttons[0x15] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 22, 7, "mute", *group );
+       buttons[0x16] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_8"];
+       button = new Button ( 23, 8, "mute", *group );
+       buttons[0x17] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 24, 1, "select", *group );
+       buttons[0x18] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 25, 2, "select", *group );
+       buttons[0x19] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 26, 3, "select", *group );
+       buttons[0x1a] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 27, 4, "select", *group );
+       buttons[0x1b] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 28, 5, "select", *group );
+       buttons[0x1c] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 29, 6, "select", *group );
+       buttons[0x1d] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 30, 7, "select", *group );
+       buttons[0x1e] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_8"];
+       button = new Button ( 31, 8, "select", *group );
+       buttons[0x1f] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 32, 1, "vselect", *group );
+       buttons[0x20] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 33, 2, "vselect", *group );
+       buttons[0x21] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 34, 3, "vselect", *group );
+       buttons[0x22] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 35, 4, "vselect", *group );
+       buttons[0x23] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 36, 5, "vselect", *group );
+       buttons[0x24] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 37, 6, "vselect", *group );
+       buttons[0x25] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 38, 7, "vselect", *group );
+       buttons[0x26] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_8"];
+       button = new Button ( 39, 8, "vselect", *group );
+       buttons[0x27] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 40, 1, "io", *group );
+       buttons[0x28] = button;
+       controls.push_back( button );
+       controls_by_name["io"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 41, 1, "sends", *group );
+       buttons[0x29] = button;
+       controls.push_back( button );
+       controls_by_name["sends"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 42, 1, "pan", *group );
+       buttons[0x2a] = button;
+       controls.push_back( button );
+       controls_by_name["pan"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 43, 1, "plugin", *group );
+       buttons[0x2b] = button;
+       controls.push_back( button );
+       controls_by_name["plugin"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 44, 1, "eq", *group );
+       buttons[0x2c] = button;
+       controls.push_back( button );
+       controls_by_name["eq"] = button;
+       group->add( *button );
+
+       group = groups["assignment"];
+       button = new Button ( 45, 1, "dyn", *group );
+       buttons[0x2d] = button;
+       controls.push_back( button );
+       controls_by_name["dyn"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 46, 1, "left", *group );
+       buttons[0x2e] = button;
+       controls.push_back( button );
+       controls_by_name["left"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 47, 1, "right", *group );
+       buttons[0x2f] = button;
+       controls.push_back( button );
+       controls_by_name["right"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 48, 1, "channel_left", *group );
+       buttons[0x30] = button;
+       controls.push_back( button );
+       controls_by_name["channel_left"] = button;
+       group->add( *button );
+
+       group = groups["bank"];
+       button = new Button ( 49, 1, "channel_right", *group );
+       buttons[0x31] = button;
+       controls.push_back( button );
+       controls_by_name["channel_right"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 50, 1, "flip", *group );
+       buttons[0x32] = button;
+       controls.push_back( button );
+       controls_by_name["flip"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 51, 1, "edit", *group );
+       buttons[0x33] = button;
+       controls.push_back( button );
+       controls_by_name["edit"] = button;
+       group->add( *button );
+
+       group = groups["display"];
+       button = new Button ( 52, 1, "name_value", *group );
+       buttons[0x34] = button;
+       controls.push_back( button );
+       controls_by_name["name_value"] = button;
+       group->add( *button );
+
+       group = groups["display"];
+       button = new Button ( 53, 1, "smpte_beats", *group );
+       buttons[0x35] = button;
+       controls.push_back( button );
+       controls_by_name["smpte_beats"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 54, 1, "F1", *group );
+       buttons[0x36] = button;
+       controls.push_back( button );
+       controls_by_name["F1"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 55, 1, "F2", *group );
+       buttons[0x37] = button;
+       controls.push_back( button );
+       controls_by_name["F2"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 56, 1, "F3", *group );
+       buttons[0x38] = button;
+       controls.push_back( button );
+       controls_by_name["F3"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 57, 1, "F4", *group );
+       buttons[0x39] = button;
+       controls.push_back( button );
+       controls_by_name["F4"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 58, 1, "F5", *group );
+       buttons[0x3a] = button;
+       controls.push_back( button );
+       controls_by_name["F5"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 59, 1, "F6", *group );
+       buttons[0x3b] = button;
+       controls.push_back( button );
+       controls_by_name["F6"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 60, 1, "F7", *group );
+       buttons[0x3c] = button;
+       controls.push_back( button );
+       controls_by_name["F7"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 61, 1, "F8", *group );
+       buttons[0x3d] = button;
+       controls.push_back( button );
+       controls_by_name["F8"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 62, 1, "F9", *group );
+       buttons[0x3e] = button;
+       controls.push_back( button );
+       controls_by_name["F9"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 63, 1, "F10", *group );
+       buttons[0x3f] = button;
+       controls.push_back( button );
+       controls_by_name["F10"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 64, 1, "F11", *group );
+       buttons[0x40] = button;
+       controls.push_back( button );
+       controls_by_name["F11"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 65, 1, "F12", *group );
+       buttons[0x41] = button;
+       controls.push_back( button );
+       controls_by_name["F12"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 66, 1, "F13", *group );
+       buttons[0x42] = button;
+       controls.push_back( button );
+       controls_by_name["F13"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 67, 1, "F14", *group );
+       buttons[0x43] = button;
+       controls.push_back( button );
+       controls_by_name["F14"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 68, 1, "F15", *group );
+       buttons[0x44] = button;
+       controls.push_back( button );
+       controls_by_name["F15"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 69, 1, "F16", *group );
+       buttons[0x45] = button;
+       controls.push_back( button );
+       controls_by_name["F16"] = button;
+       group->add( *button );
+
+       group = groups["modifiers"];
+       button = new Button ( 70, 1, "shift", *group );
+       buttons[0x46] = button;
+       controls.push_back( button );
+       controls_by_name["shift"] = button;
+       group->add( *button );
+
+       group = groups["modifiers"];
+       button = new Button ( 71, 1, "option", *group );
+       buttons[0x47] = button;
+       controls.push_back( button );
+       controls_by_name["option"] = button;
+       group->add( *button );
+
+       group = groups["modifiers"];
+       button = new Button ( 72, 1, "control", *group );
+       buttons[0x48] = button;
+       controls.push_back( button );
+       controls_by_name["control"] = button;
+       group->add( *button );
+
+       group = groups["modifiers"];
+       button = new Button ( 73, 1, "cmd_alt", *group );
+       buttons[0x49] = button;
+       controls.push_back( button );
+       controls_by_name["cmd_alt"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 74, 1, "on", *group );
+       buttons[0x4a] = button;
+       controls.push_back( button );
+       controls_by_name["on"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 75, 1, "rec_ready", *group );
+       buttons[0x4b] = button;
+       controls.push_back( button );
+       controls_by_name["rec_ready"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 76, 1, "undo", *group );
+       buttons[0x4c] = button;
+       controls.push_back( button );
+       controls_by_name["undo"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 77, 1, "snapshot", *group );
+       buttons[0x4d] = button;
+       controls.push_back( button );
+       controls_by_name["snapshot"] = button;
+       group->add( *button );
+
+       group = groups["automation"];
+       button = new Button ( 78, 1, "touch", *group );
+       buttons[0x4e] = button;
+       controls.push_back( button );
+       controls_by_name["touch"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 79, 1, "redo", *group );
+       buttons[0x4f] = button;
+       controls.push_back( button );
+       controls_by_name["redo"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 80, 1, "marker", *group );
+       buttons[0x50] = button;
+       controls.push_back( button );
+       controls_by_name["marker"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 81, 1, "enter", *group );
+       buttons[0x51] = button;
+       controls.push_back( button );
+       controls_by_name["enter"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 82, 1, "cancel", *group );
+       buttons[0x52] = button;
+       controls.push_back( button );
+       controls_by_name["cancel"] = button;
+       group->add( *button );
+
+       group = groups["functions"];
+       button = new Button ( 83, 1, "mixer", *group );
+       buttons[0x53] = button;
+       controls.push_back( button );
+       controls_by_name["mixer"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 84, 1, "frm_left", *group );
+       buttons[0x54] = button;
+       controls.push_back( button );
+       controls_by_name["frm_left"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 85, 1, "frm_right", *group );
+       buttons[0x55] = button;
+       controls.push_back( button );
+       controls_by_name["frm_right"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 86, 1, "loop", *group );
+       buttons[0x56] = button;
+       controls.push_back( button );
+       controls_by_name["loop"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 87, 1, "punch_in", *group );
+       buttons[0x57] = button;
+       controls.push_back( button );
+       controls_by_name["punch_in"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 88, 1, "punch_out", *group );
+       buttons[0x58] = button;
+       controls.push_back( button );
+       controls_by_name["punch_out"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 89, 1, "home", *group );
+       buttons[0x59] = button;
+       controls.push_back( button );
+       controls_by_name["home"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 90, 1, "end", *group );
+       buttons[0x5a] = button;
+       controls.push_back( button );
+       controls_by_name["end"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 91, 1, "rewind", *group );
+       buttons[0x5b] = button;
+       controls.push_back( button );
+       controls_by_name["rewind"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 92, 1, "ffwd", *group );
+       buttons[0x5c] = button;
+       controls.push_back( button );
+       controls_by_name["ffwd"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 93, 1, "stop", *group );
+       buttons[0x5d] = button;
+       controls.push_back( button );
+       controls_by_name["stop"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 94, 1, "play", *group );
+       buttons[0x5e] = button;
+       controls.push_back( button );
+       controls_by_name["play"] = button;
+       group->add( *button );
+
+       group = groups["transport"];
+       button = new Button ( 95, 1, "record", *group );
+       buttons[0x5f] = button;
+       controls.push_back( button );
+       controls_by_name["record"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 96, 1, "cursor_up", *group );
+       buttons[0x60] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_up"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 97, 1, "cursor_down", *group );
+       buttons[0x61] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_down"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 98, 1, "cursor_left", *group );
+       buttons[0x62] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_left"] = button;
+       group->add( *button );
+
+       group = groups["cursor"];
+       button = new Button ( 99, 1, "cursor_right", *group );
+       buttons[0x63] = button;
+       controls.push_back( button );
+       controls_by_name["cursor_right"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 100, 1, "zoom", *group );
+       buttons[0x64] = button;
+       controls.push_back( button );
+       controls_by_name["zoom"] = button;
+       group->add( *button );
+
+       group = groups["none"];
+       button = new Button ( 101, 1, "scrub", *group );
+       buttons[0x65] = button;
+       controls.push_back( button );
+       controls_by_name["scrub"] = button;
+       group->add( *button );
+
+       group = groups["user"];
+       button = new Button ( 102, 1, "user_a", *group );
+       buttons[0x66] = button;
+       controls.push_back( button );
+       controls_by_name["user_a"] = button;
+       group->add( *button );
+
+       group = groups["user"];
+       button = new Button ( 103, 1, "user_b", *group );
+       buttons[0x67] = button;
+       controls.push_back( button );
+       controls_by_name["user_b"] = button;
+       group->add( *button );
+
+       group = groups["strip_1"];
+       button = new Button ( 104, 1, "fader_touch", *group );
+       buttons[0x68] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_2"];
+       button = new Button ( 105, 2, "fader_touch", *group );
+       buttons[0x69] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_3"];
+       button = new Button ( 106, 3, "fader_touch", *group );
+       buttons[0x6a] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_4"];
+       button = new Button ( 107, 4, "fader_touch", *group );
+       buttons[0x6b] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_5"];
+       button = new Button ( 108, 5, "fader_touch", *group );
+       buttons[0x6c] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_6"];
+       button = new Button ( 109, 6, "fader_touch", *group );
+       buttons[0x6d] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_7"];
+       button = new Button ( 110, 7, "fader_touch", *group );
+       buttons[0x6e] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["strip_8"];
+       button = new Button ( 111, 8, "fader_touch", *group );
+       buttons[0x6f] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["master"];
+       button = new Button ( 112, 1, "fader_touch", *group );
+       buttons[0x70] = button;
+       controls.push_back( button );
+       group->add( *button );
+
+       group = groups["none"];
+       led = new Led ( 113, 1, "smpte", *group );
+       leds[0x71] = led;
+       controls.push_back( led );
+       controls_by_name["smpte"] = led;
+       group->add( *led );
+
+       group = groups["none"];
+       led = new Led ( 114, 1, "beats", *group );
+       leds[0x72] = led;
+       controls.push_back( led );
+       controls_by_name["beats"] = led;
+       group->add( *led );
+
+       group = groups["none"];
+       led = new Led ( 115, 1, "solo", *group );
+       leds[0x73] = led;
+       controls.push_back( led );
+       controls_by_name["solo"] = led;
+       group->add( *led );
+
+       group = groups["none"];
+       led = new Led ( 118, 1, "relay_click", *group );
+       leds[0x76] = led;
+       controls.push_back( led );
+       controls_by_name["relay_click"] = led;
+       group->add( *led );
+
+}
+
+void Mackie::MackieSurface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
+{
+       if ( bs != press && bs != release )
+       {
+               mbh.update_led( button, none );
+               return;
+       }
+       
+       LedState ls;
+       switch ( button.id() )
+       {
+
+               case 0x9028: // io
+                       switch ( bs ) {
+                               case press: ls = mbh.io_press( button ); break;
+                               case release: ls = mbh.io_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9029: // sends
+                       switch ( bs ) {
+                               case press: ls = mbh.sends_press( button ); break;
+                               case release: ls = mbh.sends_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902a: // pan
+                       switch ( bs ) {
+                               case press: ls = mbh.pan_press( button ); break;
+                               case release: ls = mbh.pan_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902b: // plugin
+                       switch ( bs ) {
+                               case press: ls = mbh.plugin_press( button ); break;
+                               case release: ls = mbh.plugin_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902c: // eq
+                       switch ( bs ) {
+                               case press: ls = mbh.eq_press( button ); break;
+                               case release: ls = mbh.eq_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902d: // dyn
+                       switch ( bs ) {
+                               case press: ls = mbh.dyn_press( button ); break;
+                               case release: ls = mbh.dyn_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902e: // left
+                       switch ( bs ) {
+                               case press: ls = mbh.left_press( button ); break;
+                               case release: ls = mbh.left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x902f: // right
+                       switch ( bs ) {
+                               case press: ls = mbh.right_press( button ); break;
+                               case release: ls = mbh.right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9030: // channel_left
+                       switch ( bs ) {
+                               case press: ls = mbh.channel_left_press( button ); break;
+                               case release: ls = mbh.channel_left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9031: // channel_right
+                       switch ( bs ) {
+                               case press: ls = mbh.channel_right_press( button ); break;
+                               case release: ls = mbh.channel_right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9032: // flip
+                       switch ( bs ) {
+                               case press: ls = mbh.flip_press( button ); break;
+                               case release: ls = mbh.flip_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9033: // edit
+                       switch ( bs ) {
+                               case press: ls = mbh.edit_press( button ); break;
+                               case release: ls = mbh.edit_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9034: // name_value
+                       switch ( bs ) {
+                               case press: ls = mbh.name_value_press( button ); break;
+                               case release: ls = mbh.name_value_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9035: // smpte_beats
+                       switch ( bs ) {
+                               case press: ls = mbh.smpte_beats_press( button ); break;
+                               case release: ls = mbh.smpte_beats_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9036: // F1
+                       switch ( bs ) {
+                               case press: ls = mbh.F1_press( button ); break;
+                               case release: ls = mbh.F1_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9037: // F2
+                       switch ( bs ) {
+                               case press: ls = mbh.F2_press( button ); break;
+                               case release: ls = mbh.F2_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9038: // F3
+                       switch ( bs ) {
+                               case press: ls = mbh.F3_press( button ); break;
+                               case release: ls = mbh.F3_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9039: // F4
+                       switch ( bs ) {
+                               case press: ls = mbh.F4_press( button ); break;
+                               case release: ls = mbh.F4_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903a: // F5
+                       switch ( bs ) {
+                               case press: ls = mbh.F5_press( button ); break;
+                               case release: ls = mbh.F5_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903b: // F6
+                       switch ( bs ) {
+                               case press: ls = mbh.F6_press( button ); break;
+                               case release: ls = mbh.F6_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903c: // F7
+                       switch ( bs ) {
+                               case press: ls = mbh.F7_press( button ); break;
+                               case release: ls = mbh.F7_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903d: // F8
+                       switch ( bs ) {
+                               case press: ls = mbh.F8_press( button ); break;
+                               case release: ls = mbh.F8_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903e: // F9
+                       switch ( bs ) {
+                               case press: ls = mbh.F9_press( button ); break;
+                               case release: ls = mbh.F9_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x903f: // F10
+                       switch ( bs ) {
+                               case press: ls = mbh.F10_press( button ); break;
+                               case release: ls = mbh.F10_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9040: // F11
+                       switch ( bs ) {
+                               case press: ls = mbh.F11_press( button ); break;
+                               case release: ls = mbh.F11_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9041: // F12
+                       switch ( bs ) {
+                               case press: ls = mbh.F12_press( button ); break;
+                               case release: ls = mbh.F12_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9042: // F13
+                       switch ( bs ) {
+                               case press: ls = mbh.F13_press( button ); break;
+                               case release: ls = mbh.F13_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9043: // F14
+                       switch ( bs ) {
+                               case press: ls = mbh.F14_press( button ); break;
+                               case release: ls = mbh.F14_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9044: // F15
+                       switch ( bs ) {
+                               case press: ls = mbh.F15_press( button ); break;
+                               case release: ls = mbh.F15_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9045: // F16
+                       switch ( bs ) {
+                               case press: ls = mbh.F16_press( button ); break;
+                               case release: ls = mbh.F16_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9046: // shift
+                       switch ( bs ) {
+                               case press: ls = mbh.shift_press( button ); break;
+                               case release: ls = mbh.shift_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9047: // option
+                       switch ( bs ) {
+                               case press: ls = mbh.option_press( button ); break;
+                               case release: ls = mbh.option_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9048: // control
+                       switch ( bs ) {
+                               case press: ls = mbh.control_press( button ); break;
+                               case release: ls = mbh.control_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9049: // cmd_alt
+                       switch ( bs ) {
+                               case press: ls = mbh.cmd_alt_press( button ); break;
+                               case release: ls = mbh.cmd_alt_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904a: // on
+                       switch ( bs ) {
+                               case press: ls = mbh.on_press( button ); break;
+                               case release: ls = mbh.on_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904b: // rec_ready
+                       switch ( bs ) {
+                               case press: ls = mbh.rec_ready_press( button ); break;
+                               case release: ls = mbh.rec_ready_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904c: // undo
+                       switch ( bs ) {
+                               case press: ls = mbh.undo_press( button ); break;
+                               case release: ls = mbh.undo_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904d: // snapshot
+                       switch ( bs ) {
+                               case press: ls = mbh.snapshot_press( button ); break;
+                               case release: ls = mbh.snapshot_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904e: // touch
+                       switch ( bs ) {
+                               case press: ls = mbh.touch_press( button ); break;
+                               case release: ls = mbh.touch_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x904f: // redo
+                       switch ( bs ) {
+                               case press: ls = mbh.redo_press( button ); break;
+                               case release: ls = mbh.redo_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9050: // marker
+                       switch ( bs ) {
+                               case press: ls = mbh.marker_press( button ); break;
+                               case release: ls = mbh.marker_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9051: // enter
+                       switch ( bs ) {
+                               case press: ls = mbh.enter_press( button ); break;
+                               case release: ls = mbh.enter_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9052: // cancel
+                       switch ( bs ) {
+                               case press: ls = mbh.cancel_press( button ); break;
+                               case release: ls = mbh.cancel_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9053: // mixer
+                       switch ( bs ) {
+                               case press: ls = mbh.mixer_press( button ); break;
+                               case release: ls = mbh.mixer_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9054: // frm_left
+                       switch ( bs ) {
+                               case press: ls = mbh.frm_left_press( button ); break;
+                               case release: ls = mbh.frm_left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9055: // frm_right
+                       switch ( bs ) {
+                               case press: ls = mbh.frm_right_press( button ); break;
+                               case release: ls = mbh.frm_right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9056: // loop
+                       switch ( bs ) {
+                               case press: ls = mbh.loop_press( button ); break;
+                               case release: ls = mbh.loop_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9057: // punch_in
+                       switch ( bs ) {
+                               case press: ls = mbh.punch_in_press( button ); break;
+                               case release: ls = mbh.punch_in_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9058: // punch_out
+                       switch ( bs ) {
+                               case press: ls = mbh.punch_out_press( button ); break;
+                               case release: ls = mbh.punch_out_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9059: // home
+                       switch ( bs ) {
+                               case press: ls = mbh.home_press( button ); break;
+                               case release: ls = mbh.home_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905a: // end
+                       switch ( bs ) {
+                               case press: ls = mbh.end_press( button ); break;
+                               case release: ls = mbh.end_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905b: // rewind
+                       switch ( bs ) {
+                               case press: ls = mbh.rewind_press( button ); break;
+                               case release: ls = mbh.rewind_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905c: // ffwd
+                       switch ( bs ) {
+                               case press: ls = mbh.ffwd_press( button ); break;
+                               case release: ls = mbh.ffwd_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905d: // stop
+                       switch ( bs ) {
+                               case press: ls = mbh.stop_press( button ); break;
+                               case release: ls = mbh.stop_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905e: // play
+                       switch ( bs ) {
+                               case press: ls = mbh.play_press( button ); break;
+                               case release: ls = mbh.play_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x905f: // record
+                       switch ( bs ) {
+                               case press: ls = mbh.record_press( button ); break;
+                               case release: ls = mbh.record_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9060: // cursor_up
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_up_press( button ); break;
+                               case release: ls = mbh.cursor_up_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9061: // cursor_down
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_down_press( button ); break;
+                               case release: ls = mbh.cursor_down_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9062: // cursor_left
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_left_press( button ); break;
+                               case release: ls = mbh.cursor_left_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9063: // cursor_right
+                       switch ( bs ) {
+                               case press: ls = mbh.cursor_right_press( button ); break;
+                               case release: ls = mbh.cursor_right_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9064: // zoom
+                       switch ( bs ) {
+                               case press: ls = mbh.zoom_press( button ); break;
+                               case release: ls = mbh.zoom_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9065: // scrub
+                       switch ( bs ) {
+                               case press: ls = mbh.scrub_press( button ); break;
+                               case release: ls = mbh.scrub_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9066: // user_a
+                       switch ( bs ) {
+                               case press: ls = mbh.user_a_press( button ); break;
+                               case release: ls = mbh.user_a_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+               case 0x9067: // user_b
+                       switch ( bs ) {
+                               case press: ls = mbh.user_b_press( button ); break;
+                               case release: ls = mbh.user_b_release( button ); break;
+                               case neither: break;
+                       }
+                       break;
+
+       }
+       mbh.update_led( button, ls );
+}
index 192af6a1ce3fe5f086b6815888ed8786eca1bae6..1c27c82be1c1e3f1cc6224f31e59ded21dead893 100644 (file)
@@ -24,6 +24,7 @@
 #include <algorithm>
 #include <cstdarg>
 #include <iomanip>
+#include <stdexcept>
 
 using namespace std;
 
@@ -96,3 +97,12 @@ ostream & operator << ( ostream & os, const MidiByteArray & mba )
        os << "]";
        return os;
 }
+
+MidiByteArray & operator << ( MidiByteArray & mba, const std::string & st )
+{
+       for ( string::const_iterator it = st.begin(); it != st.end(); ++it )
+       {
+               mba << *it;
+       }
+       return mba;
+}
index e77ece1b88a6e8d96c16b53f49ea8e0d0175442d..7176367189d3c391eed4320be02111fb06a3dcaf 100644 (file)
@@ -67,6 +67,9 @@ public:
 /// append the given byte to the end of the array
 MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b );
 
+/// append the given string to the end of the array
+MidiByteArray & operator << ( MidiByteArray & mba, const std::string & );
+
 /// append the given array to the end of this array
 MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr );
 
index c21d326fef277dce469f6c8f4e1f3fe9c7736cbf..33a8908dde4da56f833fcbb03ef78d92c3d95e2e 100644 (file)
 #include <ardour/route.h>
 #include <ardour/track.h>
 #include <ardour/panner.h>
-#include <ardour/types.h>
 
 #include "mackie_control_protocol.h"
 
 #include <stdexcept>
 
 using namespace Mackie;
+using namespace std;
 
 void RouteSignal::connect()
 {
+       back_insert_iterator<Connections> cins = back_inserter( _connections );
+       
        if ( _strip.has_solo() )
-               _solo_changed_connection = _route.solo_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_solo_changed ), this ) );
+               cins = _route.solo_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_solo_changed ), this ) );
        
        if ( _strip.has_mute() )
-               _mute_changed_connection = _route.mute_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_mute_changed ), this ) );
+               cins = _route.mute_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_mute_changed ), this ) );
        
        if ( _strip.has_gain() )
-               _gain_changed_connection = _route.gain_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_gain_changed ), this ) );
+               cins = _route.gain_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_gain_changed ), this, true ) );
                
-       _name_changed_connection = _route.NameChanged.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), this ) );
+       cins = _route.NameChanged.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), this ) );
        
-       if ( _route.panner().npanners() == 1 )
+       cins = _route.panner().Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this, true ) );
+       for ( unsigned int i = 0; i < _route.panner().npanners(); ++i )
        {
-               _panner_changed_connection = _route.panner().pan_control(0)->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this ) );
+               cins = _route.panner().streampanner (i).Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this, true ) );
        }
        
        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 ), this ) )
+               cins = dynamic_cast<ARDOUR::Track&>( _route )
+                       .rec_enable_control()
+                       ->Changed
+                       .connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_record_enable_changed ), this ) )
                ;
        }
        catch ( std::bad_cast & )
@@ -59,24 +63,28 @@ void RouteSignal::connect()
                // with can't be record-enabled
        }
 
+       // TODO this works when a currently-banked route is made inactive, but not
+       // when a route is activated which should be currently banked.
+       cins = _route.active_changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_active_changed ), this ) );
+       
        // TODO
-       // active_changed
        // SelectedChanged
        // RemoteControlIDChanged. Better handled at Session level.
 }
 
 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();
+       for ( Connections::iterator it = _connections.begin(); it != _connections.end(); ++it )
+       {
+               it->disconnect();
+       }
 }
 
 void RouteSignal::notify_all()
 {
+#ifdef DEBUG
+       cout << "RouteSignal::notify_all for " << _strip << endl;
+#endif
        if ( _strip.has_solo() )
                _mcp.notify_solo_changed( this );
        
@@ -93,4 +101,7 @@ void RouteSignal::notify_all()
        
        if ( _strip.has_recenable() )
                _mcp.notify_record_enable_changed( this );
+#ifdef DEBUG
+       cout << "RouteSignal::notify_all finish" << endl;
+#endif
 }
index 0239980fd4fad7d25d53326d27d6d8c5db06c776..01b3c97c1690cc49e0281c3901fc634a0b3cb830 100644 (file)
 
 #include <sigc++/sigc++.h>
 
+#include <vector>
+
+#include "midi_byte_array.h"
+
 class MackieControlProtocol;
 
 namespace ARDOUR {
@@ -30,7 +34,7 @@ namespace Mackie
 {
 
 class Strip;
-class MackiePort;
+class SurfacePort;
 
 /**
   This class is intended to easily create and destroy the set of
@@ -41,8 +45,8 @@ class MackiePort;
 class RouteSignal
 {
 public:
-       RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip, MackiePort & port )
-       : _route( route ), _mcp( mcp ), _strip( strip ), _port( port )
+       RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip, SurfacePort & port )
+       : _route( route ), _mcp( mcp ), _strip( strip ), _port( port ), _last_gain_written(0.0)
        {
                connect();
        }
@@ -60,20 +64,27 @@ public:
        
        const ARDOUR::Route & route() const { return _route; }
        Strip & strip() { return _strip; }
-       MackiePort & port() { return _port; }
+       SurfacePort & port() { return _port; }
+       
+       float last_gain_written() const { return _last_gain_written; }
+       void last_gain_written( float other ) { _last_gain_written = other; }
+       
+       const MidiByteArray & last_pan_written() const { return _last_pan_written; }
+       void last_pan_written( const MidiByteArray & other ) { _last_pan_written = other; }
        
 private:
        ARDOUR::Route & _route;
        MackieControlProtocol & _mcp;
        Strip & _strip;
-       MackiePort & _port;     
+       SurfacePort & _port;
+
+       typedef std::vector<sigc::connection> Connections;
+       Connections _connections;
 
-       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;
+       // Last written values for the gain and pan, to avoid overloading
+       // the midi connection to the surface
+       float _last_gain_written;
+       MidiByteArray _last_pan_written;
 };
 
 }
index e22965a906543e70307ab286121188076662293f..4dd3bfdd28591de9ec64e87966a8ef4f80be3cbd 100644 (file)
@@ -20,13 +20,13 @@ button,1,assignment,io,1,1,0x28
 button,1,assignment,sends,1,1,0x5a
 button,1,assignment,pan,1,1,0x59
 button,1,assignment,plugin,1,1,0x57
-button,1,assignment,eq,1,1,0x58
-button,1,assignment,dyn,1,1,0x2d
+button,1,functions,drop,1,1,0x58 # was eq
+button,1,assignment,zoom,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,,scrub,1,1,0x32
 button,1,,edit,1,1,0x56
 
 button,1,display,name_value,1,0,0x34
@@ -54,12 +54,13 @@ 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,snapshot,1,1,0x5f
 button,1,functions,redo,1,1,0x4f
 button,1,functions,marker,1,1,0x47
 button,1,functions,enter,1,1,0x51
 button,1,functions,cancel,1,0,0x52
 button,1,functions,mixer,1,0,0x53
+button,1,functions,save,1,0,0x4d
 
 # transport buttons
 button,1,transport,frm_left,1,1,0x5b
@@ -78,8 +79,8 @@ 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,,"dyn",1,1,0x64
+button,1,,"flip",1,1,0x65
 button,1,user,"user_a",1,0,0x66
 button,1,user,"user_b",1,0,0x67
 
index b56fd6010d6ea5277ed97b587a19eec5b102a5c8..666c34d4af6868449bba77bb717e68ed377e82d9 100644 (file)
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+this_dir = File.dirname(__FILE__)
+
 require 'faster_csv'
-require 'mackie.rb'
+require "#{this_dir}/mackie.rb"
 
 class Control
   attr_accessor :id, :led, :group, :name, :ordinal, :switch
@@ -191,6 +193,12 @@ class Surface
         end
         
         # add the new control to the various lookups
+        # but first print a warning if the id is duplicated
+        if @controls_by_id.has_key?( row.id ) && control.group.class != Strip
+          duplicated = @controls_by_id[row.id]
+          puts "duplicate id #{control.id}:#{control.name} of #{duplicated.id}:#{duplicated.name}"
+        end
+        
         @controls_by_id[row.id] = control
         @controls << control
         group << control
index 8972cba137869bc2ab2c5e9f585777e43973a123..e9ccf695f4b53d962e9c5ab791fa3fcb30c20b48 100755 (executable)
 require 'controls.rb'
 require 'mackie.rb'
 
+if ARGV.size != 2
+  puts "#$0 /dev/snd/midiXXXX control-file.csv"
+  exit 1
+end
+
 while !File.exist? ARGV[0]
   sleep 0.010
 end
@@ -30,46 +35,14 @@ puts ""
 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" )
+mck.write_sysex "\x61"
 
 # all leds off. bcf2000 doesn't respond
-#file.write( hdr + "\x62\xf7" )
+mck.write_sysex "\x62"
 
 # get version. comes back as ASCII bytes
-version = mck.sysex( "\x13\x00" )
+version = mck.sysex "\x13\x00"
 puts "version: #{version.map{|x| x.chr}}"
 
 # write a welcome message. bcf2000 responds with exact
@@ -126,7 +99,7 @@ while bytes = mck.file.read( 3 )
   control = sf.midis[midi_type][control_id]
   
   print " Control Type: %-7s, " % sf.types[midi_type]
-  print "id: %4i" % control_id
+  print "id: %4x" % control_id
   print ", control: %15s" % ( control ? control.name : "nil control" )
   print ", %15s" % ( control ? control.group.name : "nil group" )
   print "\n"
diff --git a/libs/surfaces/mackie/scripts/mackie-dump.midi b/libs/surfaces/mackie/scripts/mackie-dump.midi
new file mode 100644 (file)
index 0000000..57b3600
Binary files /dev/null and b/libs/surfaces/mackie/scripts/mackie-dump.midi differ
index a5c07f2abb4ddbc32cb68231c190ee73fa2f184b..a369ac890483fa904244faa083744aeebeb59951 100644 (file)
@@ -25,17 +25,8 @@ while !File.exist? ARGV[0]
 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}}"
+#mck = Mackie.new( file )
+device = false
 
 # respond to control movements
 while bytes = file.read( 3 )
@@ -121,7 +112,7 @@ while bytes = file.read( 3 )
   end
   
   # output bytes
-  if output
+  if device && output
     #sleep 0.1
     puts "sending: %02.x %02.x %02.x" % [ output[0], output[1], output[2] ]
     begin
index 3b29be32494479aefe838da8e95e07d165be0bce..a82a144b67756d53807edb560043b0c3f541709e 100644 (file)
@@ -52,17 +52,29 @@ void Mackie::<%= sf.name %>Surface::init_controls()
 % end
 
        // initialise controls
-       Control * control = 0;
+       Fader * fader = 0;
+       Pot * pot = 0;
+       Button * button = 0;
+       Led * led = 0;
 
 % sf.controls.each do |control|
+       <%-
+               variable_name = control.class.name.downcase
+               class_name =
+               if control.name == 'jog'
+                       'Jog'
+               else
+                       control.class.name
+               end
+       -%>
        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 );
+       <%= variable_name %> = new <%= class_name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group );
+       <%= variable_name %>s[0x<%=control.id.to_hex %>] = <%= variable_name %>;
+       controls.push_back( <%= variable_name %> );
        <%- if control.group.class != Strip -%>
-       controls_by_name["<%= control.name %>"] = control;
+       controls_by_name["<%= control.name %>"] = <%= variable_name %>;
        <%- end -%>
-       group->add( *control );
+       group->add( *<%= variable_name %> );
 
 % end
 }
@@ -82,7 +94,7 @@ void Mackie::<%= sf.name %>Surface::handle_button( MackieButtonHandler & mbh, Bu
 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 %>
+               case 0x<%= ( button.class.midi_zero_byte << 8 | 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;
index 782b0d427ca572e4ca3d2b833ef3f4302ff90901..5fd34914d82d5956ba12b8e6d2f5c45fa7be69a6 100755 (executable)
@@ -4,6 +4,6 @@ require 'controls.rb'
 require 'pp'
 
 sf = Surface.new
-sf.parse
+sf.parse( ARGV[0] )
 sf.types.each{|k,v| puts "%02.x #{v}" % k}
 
index 1110fe117cd0dc145b0f1a0c48fbdbe54a584f38..e9eb5570ce3b1a4a848a01ac035db4d33e36dde0 100644 (file)
@@ -3,7 +3,6 @@
 #include <sstream>
 #include <iomanip>
 #include <iostream>
- #include <typeinfo>
 
 using namespace std;
 using namespace Mackie;
@@ -15,8 +14,14 @@ Surface::Surface( uint32_t max_strips, uint32_t unit_strips )
 
 void Surface::init()
 {
+#ifdef DEBUG
+       cout << "Surface::init" << endl;
+#endif
        init_controls();
        init_strips( _max_strips, _unit_strips );
+#ifdef DEBUG
+       cout << "Surface::init finish" << endl;
+#endif
 }
 
 Surface::~Surface()
@@ -52,6 +57,8 @@ void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips )
                        // shallow copy existing strip
                        // which works because the controls
                        // have the same ids across units
+                       // TODO this needs to be a deep copy because
+                       // controls hold state now - in_use
                        Strip * strip = new Strip( *strips[i % unit_strips] );
                        
                        // update the relevant values
@@ -64,80 +71,3 @@ void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips )
                }
        }
 }
-
-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() );
-       }
-}
-
index 0ccde75537ffdd37600080cbf70fea2ad8706221..5305fe7eb8a7712d81fc4ed1fb965c8dfcfa8d31 100644 (file)
@@ -9,6 +9,8 @@ namespace Mackie
 {
 
 class MackieButtonHandler;
+class SurfacePort;
+class MackieMidiBuilder;
 
 /**
        This represents an entire control surface, made up of Groups,
@@ -53,11 +55,13 @@ public:
                These are alternative addressing schemes
                They use maps because the indices aren't always
                0-based.
+               
+               Indexed by raw_id not by id. @see Control for the distinction.
        */
-       std::map<int,Control*> faders;
-       std::map<int,Control*> pots;
-       std::map<int,Control*> buttons;
-       std::map<int,Control*> leds;
+       std::map<int,Fader*> faders;
+       std::map<int,Pot*> pots;
+       std::map<int,Button*> buttons;
+       std::map<int,Led*> leds;
 
        /// no strip controls in here because they usually
        /// have the same names.
@@ -79,7 +83,38 @@ public:
        
        /// map button ids to calls to press_ and release_ in mbh
        virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) = 0;
+
+public:
+       /// display an indicator of the first switched-in Route. Do nothing by default.
+       virtual void display_bank_start( SurfacePort &, MackieMidiBuilder &, uint32_t current_bank ) {};
+               
+       /// called from MackieControlPRotocol::zero_all to turn things off
+       virtual void zero_all( SurfacePort &, MackieMidiBuilder & ) {};
+
+       /// turn off leds around the jog wheel. This is for surfaces that use a pot
+       /// pretending to be a jog wheel.
+       virtual void blank_jog_ring( SurfacePort &, MackieMidiBuilder & ) {};
+
+       virtual bool has_timecode_display() const = 0;
+       virtual void display_timecode( SurfacePort &, MackieMidiBuilder &, const std::string & timecode, const std::string & timecode_last ) {};
        
+public:
+       /**
+               This is used to calculate the clicks per second that define
+               a transport speed of 1.0 for the jog wheel. 100.0 is 10 clicks
+               per second, 50.5 is 5 clicks per second.
+       */
+       virtual float scrub_scaling_factor() = 0;
+
+       /**
+               The scaling factor function for speed increase and decrease. At
+               low transport speeds this should return a small value, for high transport
+               speeds, this should return an exponentially larger value. This provides
+               high definition control at low speeds and quick speed changes to/from
+               higher speeds.
+       */
+       virtual float scaled_delta( const ControlState & state, float current_speed ) = 0;
+
 protected:
        virtual void init_controls() = 0;
        virtual void init_strips( uint32_t max_strips, uint32_t unit_strips );
index cc5c5cf13411338039a178609e30577ac435e7fc..8aaea1d5ba9ac1a7db3d5ffac04b70694cdf5fda 100644 (file)
 using namespace std;
 using namespace Mackie;
 
+SurfacePort::SurfacePort()
+: _port( 0 ), _number( 0 ), _active( false )
+{
+}
+
 SurfacePort::SurfacePort( MIDI::Port & port, int number )
-: _port( port ), _number( number ), _active( false )
+: _port( &port ), _number( number ), _active( false )
 {
 }
 
 SurfacePort::~SurfacePort()
 {
-       //cout << "~SurfacePort::SurfacePort()" << endl;
+#ifdef PORT_DEBUG
+       cout << "~SurfacePort::SurfacePort()" << endl;
+#endif
        // make sure another thread isn't reading or writing as we close the port
        Glib::RecMutex::Lock lock( _rwlock );
        _active = false;
-       //cout << "~SurfacePort::SurfacePort() finished" << endl;
+#ifdef PORT_DEBUG
+       cout << "~SurfacePort::SurfacePort() finished" << endl;
+#endif
 }
 
 // wrapper for one day when strerror_r is working properly
@@ -67,24 +76,29 @@ MidiByteArray SurfacePort::read()
        if ( !active() ) return retval;
        
        // return nothing read if the lock isn't acquired
+#if 0
        Glib::RecMutex::Lock lock( _rwlock, Glib::TRY_LOCK );
                
        if ( !lock.locked() )
        {
-               //cout << "SurfacePort::read not locked" << endl;
+               cout << "SurfacePort::read not locked" << endl;
                return retval;
        }
        
        // check active again - destructor sequence
        if ( !active() ) return retval;
+#endif
        
        // read port and copy to return value
-       int nread = port().read (buf, sizeof (buf));
+       int nread = port().read( buf, sizeof (buf) );
 
        if (nread >= 0) {
                retval.copy( nread, buf );
                if ((size_t) nread == sizeof (buf))
                {
+#ifdef PORT_DEBUG
+                       cout << "SurfacePort::read recursive" << endl;
+#endif
                        retval << read();
                }
        }
@@ -101,13 +115,17 @@ MidiByteArray SurfacePort::read()
                        throw MackieControlException( os.str() );
                }
        }
+#ifdef PORT_DEBUG
+       cout << "SurfacePort::read: " << retval << endl;
+#endif
        return retval;
 }
 
 void SurfacePort::write( const MidiByteArray & mba )
 {
-       //if ( mba[0] == 0xf0 ) cout << "SurfacePort::write: " << mba << endl;
-       //cout << "SurfacePort::write: " << mba << endl;
+#ifdef PORT_DEBUG
+       cout << "SurfacePort::write: " << mba << endl;
+#endif
        
        // check active before and after lock - to make sure
        // that the destructor doesn't destroy the mutex while
@@ -116,21 +134,26 @@ void SurfacePort::write( const MidiByteArray & mba )
        Glib::RecMutex::Lock lock( _rwlock );
        if ( !active() ) return;
 
-       int count = port().write( mba.bytes().get(), mba.size(), 0 );
+       int count = port().write( mba.bytes().get(), mba.size(), 0);
        if ( count != (int)mba.size() )
        {
-               if ( errno != EAGAIN )
+               if ( errno == 0 )
+               {
+                       cout << "port overflow on " << port().name() << ". Did not write all of " << mba << endl;
+               }
+               else if ( errno != EAGAIN )
                {
                        ostringstream os;
                        os << "Surface: couldn't write to port " << port().name();
-                       os << ": " << errno << fetch_errmsg( errno );
+                       os << ", error: " << fetch_errmsg( errno ) << "(" << errno << ")";
                        
-                       cout << os.str();
+                       cout << os.str() << endl;
                        inactive_event();
-                       throw MackieControlException( os.str() );
                }
        }
-       //if ( mba[0] == 0xf0 ) cout << "SurfacePort::write " << count << endl;
+#ifdef PORT_DEBUG
+       cout << "SurfacePort::wrote " << count << endl;
+#endif
 }
 
 void SurfacePort::write_sysex( const MidiByteArray & mba )
@@ -147,22 +170,6 @@ void SurfacePort::write_sysex( MIDI::byte msg )
        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 << "; ";
-       return os;
-}
-
 ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
 {
        os << "{ ";
index 87419f1bcdb08ae494d250b15a046e5921d1dcb6..421446df68b688a2bd13cb7847416e7bde9d748b 100644 (file)
@@ -48,20 +48,20 @@ public:
 
        /// 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();
+       virtual MidiByteArray read();
        
        /// an easier way to output bytes via midi
-       void write( const MidiByteArray & );
+       virtual 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
+       /// 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; }
+       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;
@@ -85,8 +85,12 @@ public:
        virtual bool active() const { return _active; }
        virtual void active( bool yn ) { _active = yn; }
        
+protected:
+       /// Only for use by DummyPort
+       SurfacePort();
+       
 private:
-       MIDI::Port & _port;
+       MIDI::Port * _port;
        int _number;
        bool _active;
 
diff --git a/libs/surfaces/mackie/timer.h b/libs/surfaces/mackie/timer.h
new file mode 100644 (file)
index 0000000..8887553
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+Copyright (C) 1998, 1999, 2000, 2007 John Anderson
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library 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 Library General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#ifndef timer_h
+#define timer_h
+
+#ifdef _WIN32
+#include "windows.h"
+#else
+#include <sys/time.h>
+#endif
+
+namespace Mackie
+{
+
+/**
+       millisecond timer class.
+*/
+class Timer
+{
+public:
+       
+       /**
+               start the timer running if true, or just create the
+               object if false.
+       */
+       Timer( bool shouldStart = true )
+       {
+               if ( shouldStart )
+                       start();
+       }
+       
+       /**
+               Start the timer running. Return the current timestamp, in milliseconds
+       */
+       unsigned long start()
+       {
+#ifdef _WIN32
+               _start = (unsigned long)::GetTickCount();
+#else
+               gettimeofday ( &_start, 0 );
+#endif
+               running = true;
+#ifdef _WIN32
+               return _start;
+#else
+               return ( _start.tv_sec * 1000000 + _start.tv_usec ) / 1000;
+#endif
+       }
+
+       /**
+               returns the number of milliseconds since start
+               also stops the timer running
+       */
+       unsigned long stop()
+       {
+#ifdef _WIN32
+               _stop = (unsigned long)::GetTickCount();
+#else
+               gettimeofday ( &_stop, 0 );
+#endif
+               running = false;
+               return elapsed();
+       }
+
+       /**
+               returns the number of milliseconds since start
+       */
+       unsigned long elapsed() const
+       {
+               if ( running )
+               {
+#ifdef _WIN32
+                       DWORD current = ::GetTickCount();
+                       return current - _start;
+#else
+                       struct timeval current;
+                       gettimeofday ( &current, 0 );
+                       return (
+                               ( current.tv_sec * 1000000 + current.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
+                       ) / 1000
+                       ;
+#endif
+               }
+               else
+               {
+#ifdef _WIN32
+                       return _stop - _start;
+#else
+                       return (
+                               ( _stop.tv_sec * 1000000 + _stop.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
+                       ) / 1000
+                       ;
+#endif
+               }
+       }
+       
+       /**
+               Call stop and then start. Return the value from stop.
+       */
+       unsigned long restart()
+       {
+               unsigned long retval = stop();
+               start();
+               return retval;
+       }
+
+private:
+#ifdef _WIN32
+       unsigned long _start;
+       unsigned long _stop;
+#else
+       struct timeval _start;
+       struct timeval _stop;
+#endif
+       bool running;
+};
+
+}
+
+#endif
index d2818d734025eac25e2cd5f1cd632f082c9e8ecc..b9f2e9d488c0556744fad5f92bfff2f02d9d3dc6 100644 (file)
@@ -2,8 +2,28 @@
 
 namespace Mackie
 {
-       LedState on( LedState::on );
-       LedState off( LedState::off );
-       LedState flashing( LedState::flashing );
-       LedState none( LedState::none );
+LedState on( LedState::on );
+LedState off( LedState::off );
+LedState flashing( LedState::flashing );
+LedState none( LedState::none );
+
+std::ostream & operator << ( std::ostream & os, const ControlState & cs )
+{
+       os << "ControlState { ";
+       os << "pos: " << cs.pos;
+       os << ", ";
+       os << "sign: " << cs.sign;
+       os << ", ";
+       os << "delta: " << cs.delta;
+       os << ", ";
+       os << "ticks: " << cs.ticks;
+       os << ", ";
+       os << "led_state: " << cs.led_state.state();
+       os << ", ";
+       os << "button_state: " << cs.button_state;
+       os << " }";
+
+       return os;
+}
+
 }
index 2b47e156408bfb7e2d1e1cf9f5f4d52e73a79aa7..be5c7e8b79e49295541069d0c8cae4ed11099e9f 100644 (file)
@@ -18,6 +18,8 @@
 #ifndef mackie_types_h
 #define mackie_types_h
 
+#include <iostream>
+
 namespace Mackie
 {
 
@@ -62,23 +64,34 @@ enum ButtonState { neither = -1, release = 0, press = 1 };
 */
 struct ControlState
 {
-       ControlState(): pos(0.0), delta(0.0), button_state(neither) {}
+       ControlState(): pos(0.0), sign(0), delta(0.0), ticks(0), led_state(off), 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( float flt, unsigned 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) {}
        
+       /// For faders. Between 0 and 1.
        float pos;
+               
+       /// For pots. Sign. Either -1 or 1;
+       int sign;
+
+       /// For pots. Signed value of total movement. Between 0 and 1
        float delta;
-       int ticks;
+               
+       /// For pots. Unsigned number of ticks. Usually between 1 and 16.
+       unsigned int ticks;
+               
        LedState led_state;
        ButtonState button_state;
 };
 
+std::ostream & operator << ( std::ostream &, const ControlState & );
+
 class Control;
 class Fader;
 class Button;