summaryrefslogtreecommitdiff
path: root/libs/surfaces/mackie
diff options
context:
space:
mode:
Diffstat (limited to 'libs/surfaces/mackie')
-rw-r--r--libs/surfaces/mackie/SConscript74
-rw-r--r--libs/surfaces/mackie/TODO45
-rw-r--r--libs/surfaces/mackie/bcf_surface.cc1473
-rw-r--r--libs/surfaces/mackie/bcf_surface.h27
-rw-r--r--libs/surfaces/mackie/controls.cc109
-rw-r--r--libs/surfaces/mackie/controls.h301
-rw-r--r--libs/surfaces/mackie/interface.cc96
-rw-r--r--libs/surfaces/mackie/mackie_button_handler.cc691
-rw-r--r--libs/surfaces/mackie/mackie_button_handler.h227
-rw-r--r--libs/surfaces/mackie/mackie_control_exception.h47
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc1378
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.h307
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol_poll.cc192
-rw-r--r--libs/surfaces/mackie/mackie_midi_builder.cc173
-rw-r--r--libs/surfaces/mackie/mackie_midi_builder.h81
-rw-r--r--libs/surfaces/mackie/mackie_port.cc399
-rw-r--r--libs/surfaces/mackie/mackie_port.h122
-rw-r--r--libs/surfaces/mackie/mackie_surface.cc1504
-rw-r--r--libs/surfaces/mackie/mackie_surface.h27
-rw-r--r--libs/surfaces/mackie/midi_byte_array.cc98
-rw-r--r--libs/surfaces/mackie/midi_byte_array.h76
-rw-r--r--libs/surfaces/mackie/route_signal.cc95
-rw-r--r--libs/surfaces/mackie/route_signal.h81
-rw-r--r--libs/surfaces/mackie/scripts/bank.rb32
-rw-r--r--libs/surfaces/mackie/scripts/bcf-controls.csv96
-rw-r--r--libs/surfaces/mackie/scripts/controls.rb208
-rwxr-xr-xlibs/surfaces/mackie/scripts/dump.rb11
-rw-r--r--libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb59
-rw-r--r--libs/surfaces/mackie/scripts/generate-button-handlers-h.erb54
-rwxr-xr-xlibs/surfaces/mackie/scripts/generate-surface.rb26
-rwxr-xr-xlibs/surfaces/mackie/scripts/host.rb133
-rw-r--r--libs/surfaces/mackie/scripts/mackie-controls.csv93
-rw-r--r--libs/surfaces/mackie/scripts/mackie.rb119
-rw-r--r--libs/surfaces/mackie/scripts/parse.rb61
-rw-r--r--libs/surfaces/mackie/scripts/signals.rb137
-rw-r--r--libs/surfaces/mackie/scripts/simple_host.rb137
-rw-r--r--libs/surfaces/mackie/scripts/surface-cc-template.erb95
-rw-r--r--libs/surfaces/mackie/scripts/surface-h-template.erb27
-rwxr-xr-xlibs/surfaces/mackie/scripts/test_controls.rb9
-rw-r--r--libs/surfaces/mackie/scripts/transform.rb26
-rw-r--r--libs/surfaces/mackie/scripts/write.rb10
-rw-r--r--libs/surfaces/mackie/surface.cc142
-rw-r--r--libs/surfaces/mackie/surface.h94
-rw-r--r--libs/surfaces/mackie/surface_port.cc178
-rw-r--r--libs/surfaces/mackie/surface_port.h100
-rw-r--r--libs/surfaces/mackie/test.cc25
-rw-r--r--libs/surfaces/mackie/types.cc9
-rw-r--r--libs/surfaces/mackie/types.h93
48 files changed, 9597 insertions, 0 deletions
diff --git a/libs/surfaces/mackie/SConscript b/libs/surfaces/mackie/SConscript
new file mode 100644
index 0000000000..7bd1357cb3
--- /dev/null
+++ b/libs/surfaces/mackie/SConscript
@@ -0,0 +1,74 @@
+# -*- python -*-
+
+import os
+import os.path
+import glob
+
+Import('env final_prefix install_prefix final_config_prefix libraries i18n')
+
+mackie = env.Copy()
+
+#
+# this defines the version number of libardour_mackie
+#
+
+domain = 'ardour_mackie'
+
+mackie.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
+mackie.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
+mackie.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
+mackie.Append(PACKAGE = domain)
+mackie.Append(POTFILE = domain + '.pot')
+
+mackie_files=Split("""
+interface.cc
+midi_byte_array.cc
+controls.cc
+route_signal.cc
+mackie_midi_builder.cc
+mackie_button_handler.cc
+mackie_control_protocol_poll.cc
+surface_port.cc
+mackie_port.cc
+types.cc
+surface.cc
+mackie_control_protocol.cc
+bcf_surface.cc
+mackie_surface.cc
+""")
+
+mackie.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
+mackie.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
+mackie.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
+mackie.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
+
+mackie.Merge ([
+ libraries['ardour'],
+ libraries['ardour_cp'],
+ libraries['sigc2'],
+ libraries['pbd'],
+ libraries['midi++2'],
+ libraries['xml'],
+ libraries['glib2'],
+ libraries['glibmm2'],
+ libraries['sndfile-ardour']
+ ])
+
+libardour_mackie = mackie.SharedLibrary('ardour_mackie', mackie_files)
+
+test_files = Split("""
+midi_byte_array.cc
+test.cc
+""")
+mackie_test = Program('mackie_test', test_files )
+
+if mackie['SURFACES']:
+ Default(libardour_mackie)
+ if env['NLS']:
+ i18n (mackie, mackie_files, env)
+ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2','surfaces'), libardour_mackie))
+
+env.Alias('tarball', env.Distribute (env['DISTTREE'],
+ [ 'SConscript' ] +
+ mackie_files +
+ glob.glob('po/*.po') + glob.glob('*.h')))
diff --git a/libs/surfaces/mackie/TODO b/libs/surfaces/mackie/TODO
new file mode 100644
index 0000000000..a9cb1b9878
--- /dev/null
+++ b/libs/surfaces/mackie/TODO
@@ -0,0 +1,45 @@
+* 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.
+* 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
+
+Later
+-----
+* remove commented couts
+* Queueing of writes?
+* Generic surface code to common location
+* bulk remote id changes cause too many surface updates. use Config->remote_model.
+* which bank switching - overlap or dead faders? Option?
+* signals for buttons?
+* MackieControlProtocol in namespace Mackie?
+* power-cycling of surface. fd_midiport doesn't close.
+* mix busses and/or a "bus-only" bank/mode
+* what about surfaces like Mackie C4 and BCR2000?
+
+Need UI integration
+-------------------
+* 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).
diff --git a/libs/surfaces/mackie/bcf_surface.cc b/libs/surfaces/mackie/bcf_surface.cc
new file mode 100644
index 0000000000..2aaa70fc3e
--- /dev/null
+++ b/libs/surfaces/mackie/bcf_surface.cc
@@ -0,0 +1,1473 @@
+/*
+ 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 ( "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 ( 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 ( 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 ( 44, 1, "punch_in", *group );
+ buttons[0x2c] = control;
+ controls.push_back( control );
+ controls_by_name["punch_in"] = control;
+ group->add( *control );
+
+ group = groups["transport"];
+ control = new Button ( 43, 1, "punch_out", *group );
+ buttons[0x2b] = 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 ( 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 ( 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 )
+ {
+ 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 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 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 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 0x2c: // 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 0x2b: // 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 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 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;
+
+ 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;
+
+ 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;
+
+ }
+ mbh.update_led( button, ls );
+}
diff --git a/libs/surfaces/mackie/bcf_surface.h b/libs/surfaces/mackie/bcf_surface.h
new file mode 100644
index 0000000000..a5fd3bf5a3
--- /dev/null
+++ b/libs/surfaces/mackie/bcf_surface.h
@@ -0,0 +1,27 @@
+#ifndef mackie_surface_bcf_h
+#define mackie_surface_bcf_h
+/*
+ Generated by scripts/generate-surface.rb
+*/
+
+#include "surface.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler;
+
+class BcfSurface : public Surface
+{
+public:
+ BcfSurface( uint32_t max_strips ) : Surface( max_strips )
+ {
+ }
+
+ virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
+ virtual void init_controls();
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/controls.cc b/libs/surfaces/mackie/controls.cc
new file mode 100644
index 0000000000..e9808119b2
--- /dev/null
+++ b/libs/surfaces/mackie/controls.cc
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "controls.h"
+#include "types.h"
+#include "mackie_midi_builder.h"
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+
+using namespace Mackie;
+using namespace std;
+
+void Group::add( Control & control )
+{
+ _controls.push_back( &control );
+}
+
+Strip::Strip( const std::string & name, int index )
+ : Group( name )
+ , _solo( 0 )
+ , _recenable( 0 )
+ , _mute( 0 )
+ , _select( 0 )
+ , _vselect( 0 )
+ , _fader_touch( 0 )
+ , _vpot( 0 )
+ , _gain( 0 )
+ , _index( index )
+{
+}
+
+/**
+ generated with
+
+controls[1].each do |x|
+ puts <<EOF
+#{x.class.name} & Strip::#{x.name}()
+{
+ if ( _#{x.name} == 0 )
+ throw MackieControlException( "#{x.name} is null" );
+ return *_#{x.name};
+}
+EOF
+end
+*/
+Fader & Strip::gain()
+{
+ if ( _gain == 0 )
+ throw MackieControlException( "gain is null" );
+ return *_gain;
+}
+Pot & Strip::vpot()
+{
+ if ( _vpot == 0 )
+ throw MackieControlException( "vpot is null" );
+ return *_vpot;
+}
+Button & Strip::recenable()
+{
+ if ( _recenable == 0 )
+ throw MackieControlException( "recenable is null" );
+ return *_recenable;
+}
+Button & Strip::solo()
+{
+ if ( _solo == 0 )
+ throw MackieControlException( "solo is null" );
+ return *_solo;
+}
+Button & Strip::mute()
+{
+ if ( _mute == 0 )
+ throw MackieControlException( "mute is null" );
+ return *_mute;
+}
+Button & Strip::select()
+{
+ if ( _select == 0 )
+ throw MackieControlException( "select is null" );
+ return *_select;
+}
+Button & Strip::vselect()
+{
+ if ( _vselect == 0 )
+ throw MackieControlException( "vselect is null" );
+ return *_vselect;
+}
+Button & Strip::fader_touch()
+{
+ if ( _fader_touch == 0 )
+ throw MackieControlException( "fader_touch is null" );
+ return *_fader_touch;
+}
diff --git a/libs/surfaces/mackie/controls.h b/libs/surfaces/mackie/controls.h
new file mode 100644
index 0000000000..1092c40453
--- /dev/null
+++ b/libs/surfaces/mackie/controls.h
@@ -0,0 +1,301 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef mackie_controls_h
+#define mackie_controls_h
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include "mackie_control_exception.h"
+
+namespace Mackie
+{
+
+class Control;
+
+/**
+ This is a loose group of controls, eg cursor buttons,
+ transport buttons, functions buttons etc.
+*/
+class Group
+{
+public:
+ Group( const std::string & name )
+ : _name( name )
+ {
+ }
+
+ virtual ~Group() {}
+
+ virtual bool is_strip() const
+ {
+ return false;
+ }
+
+ virtual bool is_master() const
+ {
+ return false;
+ }
+
+ virtual void add( Control & control );
+
+ const std::string & name() const
+ {
+ return _name;
+ }
+
+ // This is for Surface only
+ void name( const std::string & rhs ) { _name = rhs; }
+
+ typedef std::vector<Control*> Controls;
+ const Controls & controls() const { return _controls; }
+
+protected:
+ Controls _controls;
+
+private:
+ std::string _name;
+};
+
+class Button;
+class Pot;
+class Fader;
+
+/**
+ This is the set of controls that make up a strip.
+*/
+class Strip : public Group
+{
+public:
+ Strip( const std::string & name, int index );
+
+ virtual bool is_strip() const
+ {
+ return true;
+ }
+
+ virtual void add( Control & control );
+
+ /// This is the index of the strip
+ int index() const { return _index; }
+
+ /// This is for Surface only
+ void index( int rhs ) { _index = rhs; }
+
+ Button & solo();
+ Button & recenable();
+ Button & mute();
+ Button & select();
+ Button & vselect();
+ Button & fader_touch();
+ Pot & vpot();
+ Fader & gain();
+
+ bool has_solo() { return _solo != 0; }
+ bool has_recenable() { return _recenable != 0; }
+ bool has_mute() { return _mute != 0; }
+ bool has_select() { return _select != 0; }
+ bool has_vselect() { return _vselect != 0; }
+ bool has_fader_touch() { return _fader_touch != 0; }
+ bool has_vpot() { return _vpot != 0; }
+ bool has_gain() { return _gain != 0; }
+
+private:
+ Button * _solo;
+ Button * _recenable;
+ Button * _mute;
+ Button * _select;
+ Button * _vselect;
+ Button * _fader_touch;
+ Pot * _vpot;
+ Fader * _gain;
+ int _index;
+};
+
+class MasterStrip : public Strip
+{
+public:
+ MasterStrip( const std::string & name, int index )
+ : Strip( name, index )
+ {
+ }
+
+ virtual bool is_master() const
+ {
+ return true;
+ }
+};
+
+class Led;
+
+/**
+ The base class for controls on the surface. They deliberately
+ don't know the midi protocol for updating them.
+*/
+class Control
+{
+public:
+ enum type_t { type_fader, type_button, type_pot, type_led, type_led_ring };
+
+ Control( int id, int ordinal, std::string name, Group & group )
+ : _id( id ), _ordinal( ordinal ), _name( name ), _group( group )
+ {
+ }
+
+ virtual ~Control() {}
+
+ virtual const Led & led() const
+ {
+ throw MackieControlException( "no led available" );
+ }
+
+ /// The midi id of the control
+ int id() const
+ {
+ return _id;
+ }
+
+ /// The 1-based number of the control
+ int ordinal() const
+ {
+ return _ordinal;
+ }
+
+ const std::string & name() const
+ {
+ return _name;
+ }
+
+ const Group & group() const
+ {
+ return _group;
+ }
+
+ const Strip & strip() const
+ {
+ return dynamic_cast<const Strip&>( _group );
+ }
+
+ Strip & strip()
+ {
+ return dynamic_cast<Strip&>( _group );
+ }
+
+ virtual bool accepts_feedback() const
+ {
+ return true;
+ }
+
+ virtual type_t type() const = 0;
+
+private:
+ int _id;
+ int _ordinal;
+ std::string _name;
+ Group & _group;
+};
+
+std::ostream & operator << ( std::ostream & os, const Control & control );
+
+class Fader : public Control
+{
+public:
+ Fader( int id, int ordinal, std::string name, Group & group )
+ : Control( id, ordinal, name, group )
+ , _touch( false )
+ {
+ }
+
+ bool touch() const { return _touch; }
+
+ void touch( bool yn ) { _touch = yn; }
+
+ virtual type_t type() const { return type_fader; }
+
+private:
+ bool _touch;
+};
+
+class Led : public Control
+{
+public:
+ Led( int id, int ordinal, std::string name, Group & group )
+ : Control( id, ordinal, name, group )
+ {
+ }
+
+ virtual const Led & led() const { return *this; }
+
+ virtual type_t type() const { return type_led; }
+};
+
+class Button : public Control
+{
+public:
+ Button( int id, int ordinal, std::string name, Group & group )
+ : Control( id, ordinal, name, group )
+ , _led( id, ordinal, name + "_led", group )
+ {
+ }
+
+ virtual const Led & led() const
+ {
+ return _led;
+ }
+
+ virtual type_t type() const { return type_button; };
+
+private:
+ Led _led;
+};
+
+class LedRing : public Led
+{
+public:
+ LedRing( int id, int ordinal, std::string name, Group & group )
+ : Led( id, ordinal, name, group )
+ {
+ }
+
+ virtual type_t type() const { return type_led_ring; }
+};
+
+class Pot : public Control
+{
+public:
+ Pot( int id, int ordinal, std::string name, Group & group )
+ : Control( id, ordinal, name, group )
+ , _led_ring( id, ordinal, name + "_ring", group )
+ {
+ }
+
+ virtual type_t type() const { return type_pot; }
+
+ virtual const LedRing & led_ring() const
+ {
+ return _led_ring;
+ }
+
+private:
+ LedRing _led_ring;
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/interface.cc b/libs/surfaces/mackie/interface.cc
new file mode 100644
index 0000000000..eda485b5d6
--- /dev/null
+++ b/libs/surfaces/mackie/interface.cc
@@ -0,0 +1,96 @@
+/*
+ Copyright (C) 2006,2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include <control_protocol/control_protocol.h>
+#include "mackie_control_protocol.h"
+
+#include <pbd/error.h>
+
+#include <stdexcept>
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace std;
+
+ControlProtocol*
+new_mackie_protocol (ControlProtocolDescriptor* descriptor, Session* s)
+{
+ if ( Config->get_mmc_port_name().substr(0,3) == "mcu" )
+ {
+ error << "mcu already used as mmc port" << endmsg;
+ }
+ else if ( Config->get_mtc_port_name().substr(0,3) == "mcu" )
+ {
+ error << "mcu already used as mtc port" << endmsg;
+ }
+ else if ( Config->get_midi_port_name().substr(0,3) == "mcu" )
+ {
+ error << "mcu already used as midi port" << endmsg;
+ }
+ else
+ {
+ // no one else is using the port, so try instantiate the object
+ MackieControlProtocol * mcp = 0;
+ try
+ {
+ mcp = new MackieControlProtocol (*s);
+ mcp->set_active( true );
+ }
+ catch( exception & e )
+ {
+ error << "Error instantiating MackieControlProtocol: " << e.what() << endmsg;
+ delete mcp;
+ mcp = 0;
+ }
+ return mcp;
+ }
+ return 0;
+}
+
+void
+delete_mackie_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
+{
+ delete cp;
+}
+
+bool
+probe_mackie_protocol (ControlProtocolDescriptor* descriptor)
+{
+ return MackieControlProtocol::probe();
+}
+
+static ControlProtocolDescriptor mackie_descriptor = {
+ name : "Mackie",
+ id : "uri://ardour.org/surfaces/mackie:0",
+ ptr : 0,
+ module : 0,
+ mandatory : 0,
+ supports_feedback : true,
+ probe : probe_mackie_protocol,
+ initialize : new_mackie_protocol,
+ destroy : delete_mackie_protocol
+};
+
+
+extern "C" {
+
+ControlProtocolDescriptor*
+protocol_descriptor () {
+ return &mackie_descriptor;
+}
+
+}
diff --git a/libs/surfaces/mackie/mackie_button_handler.cc b/libs/surfaces/mackie/mackie_button_handler.cc
new file mode 100644
index 0000000000..f7ac2ab6d5
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_button_handler.cc
@@ -0,0 +1,691 @@
+/*
+ Generated by scripts/generate-button-handlers.erb
+*/
+#include "mackie_button_handler.h"
+#include "controls.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace Mackie;
+
+LedState MackieButtonHandler::default_button_press( Button & button )
+{
+ cout << "press: " << button << endl;
+ return on;
+}
+LedState MackieButtonHandler::default_button_release( Button & button )
+{
+ cout << "release: " << button << endl;
+ return off;
+}
+
+LedState MackieButtonHandler::io_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::io_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::sends_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::sends_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::pan_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::pan_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::plugin_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::plugin_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::eq_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::eq_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::dyn_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::dyn_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::left_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::left_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::right_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::right_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::channel_left_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::channel_left_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::channel_right_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::channel_right_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::flip_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::flip_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::edit_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::edit_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::name_value_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::name_value_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::smpte_beats_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::smpte_beats_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F1_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F1_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F2_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F2_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F3_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F3_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F4_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F4_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F5_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F5_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F6_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F6_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F7_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F7_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F8_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F8_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F9_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F9_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F10_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F10_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F11_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F11_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F12_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F12_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F13_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F13_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F14_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F14_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F15_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F15_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::F16_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::F16_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::shift_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::shift_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::option_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::option_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::control_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::control_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::cmd_alt_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::cmd_alt_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::on_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::on_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::rec_ready_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::rec_ready_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::undo_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::undo_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::snapshot_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::snapshot_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::touch_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::touch_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::redo_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::redo_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::marker_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::marker_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::enter_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::enter_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::cancel_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::cancel_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::mixer_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::mixer_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::frm_left_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::frm_left_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::frm_right_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::frm_right_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::loop_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::loop_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::punch_in_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::punch_in_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::punch_out_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::punch_out_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::home_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::home_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::end_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::end_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::rewind_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::rewind_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::ffwd_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::ffwd_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::stop_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::stop_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::play_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::play_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::record_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::record_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::cursor_up_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::cursor_up_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::cursor_down_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::cursor_down_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::cursor_left_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::cursor_left_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::cursor_right_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::cursor_right_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::zoom_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::zoom_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::scrub_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::scrub_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::user_a_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::user_a_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::user_b_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::user_b_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::fader_touch_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::fader_touch_release( Button & button )
+{
+ return default_button_release( button );
+}
+
+LedState MackieButtonHandler::clicking_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::clicking_release( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::global_solo_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::global_solo_release( Button & button )
+{
+ return default_button_press( button );
+}
diff --git a/libs/surfaces/mackie/mackie_button_handler.h b/libs/surfaces/mackie/mackie_button_handler.h
new file mode 100644
index 0000000000..ee4187c7ce
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_button_handler.h
@@ -0,0 +1,227 @@
+#ifndef mackie_button_handler_h
+#define mackie_button_handler_h
+/*
+ Generated by scripts/generate-button-handlers.erb
+*/
+
+#include "types.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler
+{
+public:
+ virtual ~MackieButtonHandler() {}
+
+ virtual LedState default_button_press( Button & button );
+ virtual LedState default_button_release( Button & button );
+
+ virtual void update_led( Button & button, LedState ls ) = 0;
+
+
+ virtual LedState io_press( Button & );
+ virtual LedState io_release( Button & );
+
+ virtual LedState sends_press( Button & );
+ virtual LedState sends_release( Button & );
+
+ virtual LedState pan_press( Button & );
+ virtual LedState pan_release( Button & );
+
+ virtual LedState plugin_press( Button & );
+ virtual LedState plugin_release( Button & );
+
+ virtual LedState eq_press( Button & );
+ virtual LedState eq_release( Button & );
+
+ virtual LedState dyn_press( Button & );
+ virtual LedState dyn_release( Button & );
+
+ virtual LedState left_press( Button & );
+ virtual LedState left_release( Button & );
+
+ virtual LedState right_press( Button & );
+ virtual LedState right_release( Button & );
+
+ virtual LedState channel_left_press( Button & );
+ virtual LedState channel_left_release( Button & );
+
+ virtual LedState channel_right_press( Button & );
+ virtual LedState channel_right_release( Button & );
+
+ virtual LedState flip_press( Button & );
+ virtual LedState flip_release( Button & );
+
+ virtual LedState edit_press( Button & );
+ virtual LedState edit_release( Button & );
+
+ virtual LedState name_value_press( Button & );
+ virtual LedState name_value_release( Button & );
+
+ virtual LedState smpte_beats_press( Button & );
+ virtual LedState smpte_beats_release( Button & );
+
+ virtual LedState F1_press( Button & );
+ virtual LedState F1_release( Button & );
+
+ virtual LedState F2_press( Button & );
+ virtual LedState F2_release( Button & );
+
+ virtual LedState F3_press( Button & );
+ virtual LedState F3_release( Button & );
+
+ virtual LedState F4_press( Button & );
+ virtual LedState F4_release( Button & );
+
+ virtual LedState F5_press( Button & );
+ virtual LedState F5_release( Button & );
+
+ virtual LedState F6_press( Button & );
+ virtual LedState F6_release( Button & );
+
+ virtual LedState F7_press( Button & );
+ virtual LedState F7_release( Button & );
+
+ virtual LedState F8_press( Button & );
+ virtual LedState F8_release( Button & );
+
+ virtual LedState F9_press( Button & );
+ virtual LedState F9_release( Button & );
+
+ virtual LedState F10_press( Button & );
+ virtual LedState F10_release( Button & );
+
+ virtual LedState F11_press( Button & );
+ virtual LedState F11_release( Button & );
+
+ virtual LedState F12_press( Button & );
+ virtual LedState F12_release( Button & );
+
+ virtual LedState F13_press( Button & );
+ virtual LedState F13_release( Button & );
+
+ virtual LedState F14_press( Button & );
+ virtual LedState F14_release( Button & );
+
+ virtual LedState F15_press( Button & );
+ virtual LedState F15_release( Button & );
+
+ virtual LedState F16_press( Button & );
+ virtual LedState F16_release( Button & );
+
+ virtual LedState shift_press( Button & );
+ virtual LedState shift_release( Button & );
+
+ virtual LedState option_press( Button & );
+ virtual LedState option_release( Button & );
+
+ virtual LedState control_press( Button & );
+ virtual LedState control_release( Button & );
+
+ virtual LedState cmd_alt_press( Button & );
+ virtual LedState cmd_alt_release( Button & );
+
+ virtual LedState on_press( Button & );
+ virtual LedState on_release( Button & );
+
+ virtual LedState rec_ready_press( Button & );
+ virtual LedState rec_ready_release( Button & );
+
+ virtual LedState undo_press( Button & );
+ virtual LedState undo_release( Button & );
+
+ virtual LedState snapshot_press( Button & );
+ virtual LedState snapshot_release( Button & );
+
+ virtual LedState touch_press( Button & );
+ virtual LedState touch_release( Button & );
+
+ virtual LedState redo_press( Button & );
+ virtual LedState redo_release( Button & );
+
+ virtual LedState marker_press( Button & );
+ virtual LedState marker_release( Button & );
+
+ virtual LedState enter_press( Button & );
+ virtual LedState enter_release( Button & );
+
+ virtual LedState cancel_press( Button & );
+ virtual LedState cancel_release( Button & );
+
+ virtual LedState mixer_press( Button & );
+ virtual LedState mixer_release( Button & );
+
+ virtual LedState frm_left_press( Button & );
+ virtual LedState frm_left_release( Button & );
+
+ virtual LedState frm_right_press( Button & );
+ virtual LedState frm_right_release( Button & );
+
+ virtual LedState loop_press( Button & );
+ virtual LedState loop_release( Button & );
+
+ virtual LedState punch_in_press( Button & );
+ virtual LedState punch_in_release( Button & );
+
+ virtual LedState punch_out_press( Button & );
+ virtual LedState punch_out_release( Button & );
+
+ virtual LedState home_press( Button & );
+ virtual LedState home_release( Button & );
+
+ virtual LedState end_press( Button & );
+ virtual LedState end_release( Button & );
+
+ virtual LedState rewind_press( Button & );
+ virtual LedState rewind_release( Button & );
+
+ virtual LedState ffwd_press( Button & );
+ virtual LedState ffwd_release( Button & );
+
+ virtual LedState stop_press( Button & );
+ virtual LedState stop_release( Button & );
+
+ virtual LedState play_press( Button & );
+ virtual LedState play_release( Button & );
+
+ virtual LedState record_press( Button & );
+ virtual LedState record_release( Button & );
+
+ virtual LedState cursor_up_press( Button & );
+ virtual LedState cursor_up_release( Button & );
+
+ virtual LedState cursor_down_press( Button & );
+ virtual LedState cursor_down_release( Button & );
+
+ virtual LedState cursor_left_press( Button & );
+ virtual LedState cursor_left_release( Button & );
+
+ virtual LedState cursor_right_press( Button & );
+ virtual LedState cursor_right_release( Button & );
+
+ virtual LedState zoom_press( Button & );
+ virtual LedState zoom_release( Button & );
+
+ virtual LedState scrub_press( Button & );
+ virtual LedState scrub_release( Button & );
+
+ virtual LedState user_a_press( Button & );
+ virtual LedState user_a_release( Button & );
+
+ virtual LedState user_b_press( Button & );
+ virtual LedState user_b_release( Button & );
+
+ virtual LedState fader_touch_press( Button & );
+ virtual LedState fader_touch_release( Button & );
+
+ virtual LedState clicking_press( Button & );
+ virtual LedState clicking_release( Button & );
+
+ virtual LedState global_solo_press( Button & );
+ virtual LedState global_solo_release( Button & );
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/mackie_control_exception.h b/libs/surfaces/mackie/mackie_control_exception.h
new file mode 100644
index 0000000000..afe7016948
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_control_exception.h
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef mackie_control_exception_h
+#define mackie_control_exception_h
+
+#include <stdexcept>
+
+namespace Mackie
+{
+
+class MackieControlException : public std::exception
+{
+public:
+ MackieControlException( const std::string & msg )
+ : _msg( msg )
+ {
+ }
+
+ virtual ~MackieControlException() throw () {}
+
+ const char * what() const throw ()
+ {
+ return _msg.c_str();
+ }
+
+private:
+ std::string _msg;
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
new file mode 100644
index 0000000000..81d249588e
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -0,0 +1,1378 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <algorithm>
+#include <cmath>
+#include <sstream>
+#include <vector>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <float.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <boost/shared_array.hpp>
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <midi++/manager.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/error.h>
+
+#include <ardour/route.h>
+#include <ardour/session.h>
+#include <ardour/location.h>
+#include <ardour/dB.h>
+#include <ardour/panner.h>
+
+#include "mackie_control_protocol.h"
+
+#include "midi_byte_array.h"
+#include "mackie_control_exception.h"
+#include "route_signal.h"
+#include "mackie_midi_builder.h"
+#include "surface_port.h"
+#include "surface.h"
+#include "bcf_surface.h"
+#include "mackie_surface.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace sigc;
+using namespace Mackie;
+using namespace PBD;
+
+using boost::shared_ptr;
+
+#include "i18n.h"
+
+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 )
+ , connections_back( _connections )
+ , _surface( 0 )
+ , _ports_changed( false )
+ , _polling( true )
+ , pfd( 0 )
+ , nfds( 0 )
+{
+ //cout << "MackieControlProtocol::MackieControlProtocol" << endl;
+ // 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;
+ try
+ {
+ close();
+ }
+ catch ( exception & e )
+ {
+ cout << "~MackieControlProtocol caught " << e.what() << endl;
+ }
+ catch ( ... )
+ {
+ cout << "~MackieControlProtocol caught unknown" << endl;
+ }
+ //cout << "finished ~MackieControlProtocol::MackieControlProtocol" << endl;
+}
+
+Mackie::Surface & MackieControlProtocol::surface()
+{
+ if ( _surface == 0 )
+ {
+ throw MackieControlException( "_surface is 0 in MackieControlProtocol::surface" );
+ }
+ return *_surface;
+}
+
+const Mackie::MackiePort & MackieControlProtocol::mcu_port() const
+{
+ return dynamic_cast<const MackiePort &>( *_ports[0] );
+}
+
+Mackie::MackiePort & MackieControlProtocol::mcu_port()
+{
+ return dynamic_cast<const MackiePort &>( *_ports[0] );
+}
+
+// go to the previous track.
+// Assume that get_sorted_routes().size() > route_table.size()
+void MackieControlProtocol::prev_track()
+{
+ if ( _current_initial_bank >= 1 )
+ {
+ session->set_dirty();
+ switch_banks( _current_initial_bank - 1 );
+ }
+}
+
+// go to the next track.
+// Assume that get_sorted_routes().size() > route_table.size()
+void MackieControlProtocol::next_track()
+{
+ Sorted sorted = get_sorted_routes();
+ if ( _current_initial_bank + route_table.size() < sorted.size() )
+ {
+ session->set_dirty();
+ switch_banks( _current_initial_bank + 1 );
+ }
+}
+
+void MackieControlProtocol::clear_route_signals()
+{
+ for( RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it )
+ {
+ delete *it;
+ }
+ route_signals.clear();
+}
+
+// return the port for a given id - 0 based
+// throws an exception if no port found
+MackiePort & MackieControlProtocol::port_for_id( uint32_t index )
+{
+ uint32_t current_max = 0;
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ current_max += (*it)->strips();
+ if ( index < current_max ) return **it;
+ }
+
+ // oops - no matching port
+ ostringstream os;
+ os << "No port for index " << index;
+ throw MackieControlException( os.str() );
+}
+
+// predicate for sort call in get_sorted_routes
+struct RouteByRemoteId
+{
+ bool operator () ( const shared_ptr<Route> & a, const shared_ptr<Route> & b ) const
+ {
+ return a->remote_control_id() < b->remote_control_id();
+ }
+
+ bool operator () ( const Route & a, const Route & b ) const
+ {
+ return a.remote_control_id() < b.remote_control_id();
+ }
+
+ bool operator () ( const Route * a, const Route * b ) const
+ {
+ return a->remote_control_id() < b->remote_control_id();
+ }
+};
+
+MackieControlProtocol::Sorted MackieControlProtocol::get_sorted_routes()
+{
+ Sorted sorted;
+
+ // fetch all routes
+ boost::shared_ptr<Session::RouteList> routes = session->get_routes();
+ set<uint32_t> remote_ids;
+
+ // routes with remote_id 0 should never be added
+ // TODO verify this with ardour devs
+ // remote_ids.insert( 0 );
+
+ // sort in remote_id order, and exclude master, control and hidden routes
+ // and any routes that are already set.
+ for ( Session::RouteList::iterator it = routes->begin(); it != routes->end(); ++it )
+ {
+ Route & route = **it;
+ if (
+ route.active()
+ && !route.master()
+ && !route.hidden()
+ && !route.control()
+ && remote_ids.find( route.remote_control_id() ) == remote_ids.end()
+ )
+ {
+ sorted.push_back( *it );
+ remote_ids.insert( route.remote_control_id() );
+ }
+ }
+ sort( sorted.begin(), sorted.end(), RouteByRemoteId() );
+ return sorted;
+}
+
+void MackieControlProtocol::refresh_current_bank()
+{
+ switch_banks( _current_initial_bank );
+}
+
+void MackieControlProtocol::switch_banks( int initial )
+{
+ // DON'T prevent bank switch if initial == _current_initial_bank
+ // because then this method can't be used as a refresh
+
+ // sanity checking
+ Sorted sorted = get_sorted_routes();
+ int delta = sorted.size() - route_table.size();
+ if ( initial < 0 || ( delta > 0 && initial > delta ) )
+ {
+ cout << "not switching to " << initial << endl;
+ return;
+ }
+ _current_initial_bank = initial;
+
+ // first clear the signals from old routes
+ // taken care of by the RouteSignal destructors
+ clear_route_signals();
+
+ // now set the signals for new routes
+ if ( _current_initial_bank <= sorted.size() )
+ {
+ // fetch the bank start and end to switch to
+ 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;
+
+ // link routes to strips
+ uint32_t i = 0;
+ for ( ; it != end && it != sorted.end(); ++it, ++i )
+ {
+ 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;
+ route_table[i] = route;
+ RouteSignal * rs = new RouteSignal( *route, *this, strip, port_for_id(i) );
+ route_signals.push_back( rs );
+ // update strip from route
+ rs->notify_all();
+ }
+
+ // create dead strips if there aren't enough routes to
+ // fill a bank
+ for ( ; i < route_table.size(); ++i )
+ {
+ Strip & strip = *surface().strips[i];
+ // send zero for this strip
+ port_for_id(i).write( builder.zero_strip( 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 ) );
+ }
+ }
+}
+
+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" ) );
+ }
+
+ // 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 ) );
+ }
+
+ // 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 ) );
+ }
+
+ // turn off global buttons and leds
+ // global buttons are only ever on mcu_port, so we don't have
+ // to figure out which port.
+ for ( Surface::Controls::iterator it = surface().controls.begin(); it != surface().controls.end(); ++it )
+ {
+ Control & control = **it;
+ if ( !control.group().is_strip() && control.accepts_feedback() )
+ {
+ mcu_port().write( builder.zero_control( control ) );
+ }
+ }
+}
+
+int MackieControlProtocol::set_active (bool yn)
+{
+ if ( yn != _active )
+ {
+ try
+ {
+ // the reason for the locking and unlocking is that
+ // glibmm can't do a condition wait on a RecMutex
+ if ( yn )
+ {
+ // TODO what happens if this fails half way?
+
+ // create MackiePorts
+ {
+ Glib::Mutex::Lock lock( update_mutex );
+ create_ports();
+ }
+
+ // make sure the ports are being listened to
+ update_ports();
+
+ // wait until poll thread is running, with ports to poll
+ // the mutex is only there because conditions require a mutex
+ {
+ Glib::Mutex::Lock lock( update_mutex );
+ while ( nfds == 0 ) update_cond.wait( update_mutex );
+ }
+
+ // now initialise MackiePorts - ie exchange sysex messages
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ (*it)->open();
+ }
+
+ // wait until all ports are active
+ // TODO a more sophisticated approach would
+ // allow things to start up with only an MCU, even if
+ // extenders were specified but not responding.
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ (*it)->wait_for_init();
+ }
+
+ // create surface object. This depends on the ports being
+ // correctly initialised
+ initialize_surface();
+ connect_session_signals();
+
+ // yeehah!
+ _active = true;
+
+ // send current control positions to surface
+ // must come after _active = true otherwise it won't run
+ update_surface();
+ }
+ else
+ {
+ close();
+ _active = false;
+ }
+ }
+ catch( exception & e )
+ {
+ cout << "set_active to false because exception caught: " << e.what() << endl;
+ _active = false;
+ throw;
+ }
+ }
+
+ return 0;
+}
+
+bool MackieControlProtocol::handle_strip_button( Control & control, ButtonState bs, boost::shared_ptr<Route> route )
+{
+ bool state = false;
+
+ if ( bs == press )
+ {
+ if ( control.name() == "recenable" )
+ {
+ state = !route->record_enabled();
+ route->set_record_enable( state, this );
+ }
+ else if ( control.name() == "mute" )
+ {
+ state = !route->muted();
+ route->set_mute( state, this );
+ }
+ else if ( control.name() == "solo" )
+ {
+ state = !route->soloed();
+ route->set_solo( state, this );
+ }
+ else if ( control.name() == "select" )
+ {
+ // TODO make the track selected. Whatever that means.
+ //state = default_button_press( dynamic_cast<Button&>( control ) );
+ }
+ else if ( control.name() == "vselect" )
+ {
+ // TODO could be used to select different things to apply the pot to?
+ //state = default_button_press( dynamic_cast<Button&>( control ) );
+ }
+ }
+
+ if ( control.name() == "fader_touch" )
+ {
+ state = bs == press;
+ control.strip().gain().touch( state );
+ }
+
+ return state;
+}
+
+void MackieControlProtocol::update_led( Mackie::Button & button, Mackie::LedState ls )
+{
+ MackiePort * port = 0;
+ if ( button.group().is_strip() )
+ {
+ if ( button.group().is_master() )
+ {
+ port = &mcu_port();
+ }
+ else
+ {
+ port = &port_for_id( dynamic_cast<const Strip&>( button.group() ).index() );
+ }
+ }
+ else
+ {
+ port = &mcu_port();
+ }
+ 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() )
+ {
+ Button * button = dynamic_cast<Button*>( surface().controls_by_name[name] );
+ mcu_port().write( builder.build_led( button->led(), ls ) );
+ }
+ else
+ {
+ cout << "Button " << name << " not found" << endl;
+ }
+}
+
+// send messages to surface to set controls to correct values
+void MackieControlProtocol::update_surface()
+{
+ if ( _active )
+ {
+ // do the initial bank switch to connect signals
+ // _current_initial_bank is initialised by set_state
+ switch_banks( _current_initial_bank );
+
+ // create a RouteSignal for the master route
+ // but only the first time around
+ master_route_signal = shared_ptr<RouteSignal>( new RouteSignal( *master_route(), *this, master_strip(), mcu_port() ) );
+ // update strip from route
+ master_route_signal->notify_all();
+
+ // update global buttons and displays
+ notify_record_state_changed();
+ notify_transport_state_changed();
+ }
+}
+
+void MackieControlProtocol::connect_session_signals()
+{
+ // receive routes added
+ connections_back = session->RouteAdded.connect( ( mem_fun (*this, &MackieControlProtocol::notify_route_added) ) );
+ // receive record state toggled
+ connections_back = session->RecordStateChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_record_state_changed) ) );
+ // receive transport state changed
+ connections_back = session->TransportStateChange.connect( ( mem_fun (*this, &MackieControlProtocol::notify_transport_state_changed) ) );
+ // receive punch-in and punch-out
+ connections_back = Config->ParameterChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_parameter_changed) ) );
+ // receive rude solo changed
+ connections_back = session->SoloActive.connect( ( mem_fun (*this, &MackieControlProtocol::notify_solo_active_changed) ) );
+
+ // make sure remote id changed signals reach here
+ // see also notify_route_added
+ Sorted sorted = get_sorted_routes();
+ for ( Sorted::iterator it = sorted.begin(); it != sorted.end(); ++it )
+ {
+ connections_back = (*it)->RemoteControlIDChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_remote_id_changed) ) );
+ }
+}
+
+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;
+}
+
+void MackieControlProtocol::create_ports()
+{
+ MIDI::Manager * mm = MIDI::Manager::instance();
+
+ // open main port
+ {
+ MIDI::Port * midi_port = mm->port( default_port_name );
+
+ if ( midi_port == 0 ) {
+ ostringstream os;
+ os << string_compose( _("no MIDI port named \"%1\" exists - Mackie control disabled"), default_port_name );
+ error << os.str() << endmsg;
+ throw MackieControlException( os.str() );
+ }
+ add_port( *midi_port, 0 );
+ }
+
+ // open extender ports. Up to 9. Should be enough.
+ // could also use mm->get_midi_ports()
+ string ext_port_base = "mcu_xt_";
+ for ( int index = 1; index <= 9; ++index )
+ {
+ ostringstream os;
+ os << ext_port_base << index;
+ MIDI::Port * midi_port = mm->port( os.str() );
+ if ( midi_port != 0 ) add_port( *midi_port, index );
+ }
+}
+
+shared_ptr<Route> MackieControlProtocol::master_route()
+{
+ shared_ptr<Route> retval;
+ retval = session->route_by_name( "master" );
+ if ( retval == 0 )
+ {
+ // TODO search through all routes for one with the master attribute set
+ }
+ return retval;
+}
+
+Strip & MackieControlProtocol::master_strip()
+{
+ return dynamic_cast<Strip&>( *surface().groups["master"] );
+}
+
+void MackieControlProtocol::initialize_surface()
+{
+ // set up the route table
+ int strips = 0;
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ strips += (*it)->strips();
+ }
+
+ set_route_table_size( strips );
+
+ switch ( mcu_port().emulation() )
+ {
+ 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->init();
+
+ // Connect events. Must be after route table otherwise there will be trouble
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ connections_back = (*it)->control_event.connect( ( mem_fun (*this, &MackieControlProtocol::handle_control_event) ) );
+ }
+}
+
+void MackieControlProtocol::close()
+{
+ // TODO disconnect port active/inactive signals
+ // Or at least put a lock here
+
+ // disconnect global signals from Session
+ // TODO Since *this is a sigc::trackable, this shouldn't be necessary
+ // but it is for some reason
+#if 0
+ for( vector<sigc::connection>::iterator it = _connections.begin(); it != _connections.end(); ++it )
+ {
+ it->disconnect();
+ }
+#endif
+
+ if ( _surface != 0 )
+ {
+ // These will fail if the port has gone away.
+ // So catch the exception and do the rest of the
+ // close afterwards
+ // because the bcf doesn't respond to the next 3 sysex messages
+ try
+ {
+ zero_all();
+ }
+ catch ( exception & e )
+ {
+ cout << "MackieControlProtocol::close caught exception: " << e.what() << endl;
+ }
+
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ try
+ {
+ MackiePort & port = **it;
+ // faders to minimum
+ port.write_sysex( 0x61 );
+ // All LEDs off
+ port.write_sysex( 0x62 );
+ // Reset (reboot into offline mode)
+ port.write_sysex( 0x63 );
+ }
+ catch ( exception & e )
+ {
+ cout << "MackieControlProtocol::close caught exception: " << e.what() << endl;
+ }
+ }
+
+ // disconnect routes from strips
+ clear_route_signals();
+
+ delete _surface;
+ _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 )
+ {
+ delete *it;
+ }
+ _ports.clear();
+
+ // this is done already in monitor_work. But it's here so we know.
+ delete[] pfd;
+ pfd = 0;
+ nfds = 0;
+}
+
+void* MackieControlProtocol::_monitor_work (void* arg)
+{
+ return static_cast<MackieControlProtocol*>(arg)->monitor_work ();
+}
+
+XMLNode & MackieControlProtocol::get_state()
+{
+ //cout << "MackieControlProtocol::get_state" << endl;
+
+ // add name of protocol
+ XMLNode* node = new XMLNode( X_("Protocol") );
+ node->add_property( X_("name"), _name );
+
+ // add current bank
+ ostringstream os;
+ os << _current_initial_bank;
+ node->add_property( X_("bank"), os.str() );
+
+ return *node;
+}
+
+int MackieControlProtocol::set_state( const XMLNode & node )
+{
+ //cout << "MackieControlProtocol::set_state: active " << _active << endl;
+ int retval = 0;
+
+ // fetch current bank
+ if ( node.property( X_("bank") ) != 0 )
+ {
+ string bank = node.property( X_("bank") )->value();
+ try
+ {
+ set_active( true );
+ uint32_t new_bank = atoi( bank.c_str() );
+ if ( _current_initial_bank != new_bank ) switch_banks( new_bank );
+ }
+ catch ( exception & e )
+ {
+ cout << "exception in MackieControlProtocol::set_state: " << e.what() << endl;
+ return -1;
+ }
+ }
+
+ return retval;
+}
+
+void MackieControlProtocol::handle_control_event( SurfacePort & port, Control & control, const ControlState & state )
+{
+ uint32_t index = control.ordinal() - 1 + ( port.number() * port.strips() );
+ boost::shared_ptr<Route> route;
+ if ( control.group().is_strip() )
+ {
+ if ( control.group().is_master() )
+ {
+ 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;
+ }
+
+ // This handles control element events from the surface
+ // the state of the controls on the surface is usually updated
+ // from UI events.
+ 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 )
+ {
+ 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 ) );
+ }
+ }
+ break;
+
+ case Control::type_button:
+ if ( control.group().is_strip() )
+ {
+ // strips
+ if ( route != 0 )
+ {
+ handle_strip_button( control, state.button_state, route );
+ }
+ else
+ {
+ // no route so always switch the light off
+ // because no signals will be emitted by a non-route
+ port.write( builder.build_led( control.led(), off ) );
+ }
+ }
+ else if ( control.group().is_master() )
+ {
+ // master fader touch
+ boost::shared_ptr<Route> route = master_route();
+ if ( route )
+ handle_strip_button( control, state.button_state, route );
+ }
+ else
+ {
+ // handle all non-strip buttons
+ surface().handle_button( *this, state.button_state, dynamic_cast<Button&>( control ) );
+ }
+ break;
+
+ // pot (jog wheel, external control)
+ case Control::type_pot:
+ if ( control.group().is_strip() )
+ {
+ if ( route != 0 )
+ {
+ if ( route->panner().size() == 1 )
+ {
+ // assume pan for now
+ float xpos;
+ route->panner()[0]->get_effective_position (xpos);
+
+ // calculate new value, and trim
+ xpos += state.delta;
+ if ( xpos > 1.0 )
+ xpos = 1.0;
+ else if ( xpos < 0.0 )
+ xpos = 0.0;
+
+ route->panner()[0]->set_position( xpos );
+ }
+ }
+ else
+ {
+ // it's a pot for an umnapped route, so turn all the lights off
+ port.write( builder.build_led_ring( dynamic_cast<Pot &>( control ), off ) );
+ }
+ }
+ else
+ {
+ if ( control.name() == "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 ) );
+ }
+ else
+ {
+ cout << "external controller" << state.ticks << endl;
+ }
+ }
+ break;
+
+ default:
+ cout << "Control::type not handled: " << control.type() << endl;
+ }
+}
+
+/////////////////////////////////////////////////
+// handlers for Route signals
+// TODO should these be part of RouteSignal?
+// They started off as sigc handlers for signals
+// from Route, but they're also used in polling for automation
+/////////////////////////////////////////////////
+
+void MackieControlProtocol::notify_solo_changed( RouteSignal * route_signal )
+{
+ try
+ {
+ Button & button = route_signal->strip().solo();
+ route_signal->port().write( builder.build_led( button, route_signal->route().soloed() ) );
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+void MackieControlProtocol::notify_mute_changed( RouteSignal * route_signal )
+{
+ try
+ {
+ Button & button = route_signal->strip().mute();
+ route_signal->port().write( builder.build_led( button, route_signal->route().muted() ) );
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+void MackieControlProtocol::notify_record_enable_changed( RouteSignal * route_signal )
+{
+ try
+ {
+ Button & button = route_signal->strip().recenable();
+ route_signal->port().write( builder.build_led( button, route_signal->route().record_enabled() ) );
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+void MackieControlProtocol::notify_gain_changed( RouteSignal * route_signal )
+{
+ try
+ {
+ Fader & fader = route_signal->strip().gain();
+ if ( !fader.touch() )
+ {
+ route_signal->port().write( builder.build_fader( fader, gain_to_slider_position( route_signal->route().effective_gain() ) ) );
+ }
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+void MackieControlProtocol::notify_name_changed( void *, RouteSignal * route_signal )
+{
+ try
+ {
+ // TODO implement MackieControlProtocol::notify_name_changed
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+// TODO deal with > 1 channel being panned
+void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal )
+{
+ try
+ {
+ Pot & pot = route_signal->strip().vpot();
+
+ if ( route_signal->route().panner().size() == 1 )
+ {
+ float pos;
+ route_signal->route().panner()[0]->get_effective_position( pos);
+ route_signal->port().write( builder.build_led_ring( pot, ControlState( on, pos ) ) );
+ }
+ else
+ {
+ route_signal->port().write( builder.zero_control( pot ) );
+ }
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+// TODO handle plugin automation polling
+void MackieControlProtocol::update_automation( RouteSignal & rs )
+{
+ ARDOUR::AutoState gain_state = rs.route().gain_automation_state();
+ if ( gain_state == Touch || gain_state == Play )
+ {
+ notify_gain_changed( &rs );
+ }
+
+ 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 )
+ {
+ // 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 );
+ }
+}
+
+/////////////////////////////////////
+// Transport Buttons
+/////////////////////////////////////
+
+LedState MackieControlProtocol::rewind_press( Button & button )
+{
+ // can use first_mark_before/after as well
+ Location * loc = session->locations()->first_location_before (
+ session->transport_frame()
+ );
+ if ( loc != 0 ) session->request_locate( loc->start(), session->transport_rolling() );
+ return on;
+}
+
+LedState MackieControlProtocol::rewind_release( Button & button )
+{
+ return off;
+}
+
+LedState MackieControlProtocol::ffwd_press( Button & button )
+{
+ // can use first_mark_before/after as well
+ Location * loc = session->locations()->first_location_after (
+ session->transport_frame()
+ );
+ if ( loc != 0 ) session->request_locate( loc->start(), session->transport_rolling() );
+ return on;
+}
+
+LedState MackieControlProtocol::ffwd_release( Button & button )
+{
+ return off;
+}
+
+LedState MackieControlProtocol::stop_press( Button & button )
+{
+ session->request_stop();
+ return on;
+}
+
+LedState MackieControlProtocol::stop_release( Button & button )
+{
+ return session->transport_stopped();
+}
+
+LedState MackieControlProtocol::play_press( Button & button )
+{
+ session->request_transport_speed( 1.0 );
+ return on;
+}
+
+LedState MackieControlProtocol::play_release( Button & button )
+{
+ return session->transport_rolling();
+}
+
+LedState MackieControlProtocol::record_press( Button & button )
+{
+ if ( session->get_record_enabled() )
+ session->disable_record( false );
+ else
+ session->maybe_enable_record();
+ return on;
+}
+
+LedState MackieControlProtocol::record_release( Button & button )
+{
+ if ( session->get_record_enabled() )
+ {
+ if ( session->transport_rolling() )
+ return on;
+ else
+ return flashing;
+ }
+ else
+ return off;
+}
+
+///////////////////////////////////////////
+// Session signals
+///////////////////////////////////////////
+
+void MackieControlProtocol::notify_parameter_changed( const char * name_str )
+{
+ string name( name_str );
+ if ( name == "punch-in" )
+ {
+ update_global_button( "punch_in", Config->get_punch_in() );
+ }
+ else if ( name == "punch-out" )
+ {
+ update_global_button( "punch_out", Config->get_punch_out() );
+ }
+ else if ( name == "clicking" )
+ {
+ update_global_button( "clicking", Config->get_clicking() );
+ }
+ else
+ {
+ cout << "parameter changed: " << name << endl;
+ }
+}
+
+// RouteList is the set of routes that have just been added
+void MackieControlProtocol::notify_route_added( ARDOUR::Session::RouteList & rl )
+{
+ // currently assigned banks are less than the full set of
+ // strips, so activate the new strip now.
+ if ( route_signals.size() < route_table.size() )
+ {
+ refresh_current_bank();
+ }
+ // otherwise route added, but current bank needs no updating
+
+ // make sure remote id changes in the new route are handled
+ typedef ARDOUR::Session::RouteList ARS;
+ for ( ARS::iterator it = rl.begin(); it != rl.end(); ++it )
+ {
+ connections_back = (*it)->RemoteControlIDChanged.connect( ( mem_fun (*this, &MackieControlProtocol::notify_remote_id_changed) ) );
+ }
+}
+
+void MackieControlProtocol::notify_solo_active_changed( bool active )
+{
+ Button * rude_solo = reinterpret_cast<Button*>( surface().controls_by_name["solo"] );
+ mcu_port().write( builder.build_led( *rude_solo, active ? flashing : off ) );
+}
+
+void MackieControlProtocol::notify_remote_id_changed()
+{
+ Sorted sorted = get_sorted_routes();
+
+ // if a remote id has been moved off the end, we need to shift
+ // the current bank backwards.
+ if ( sorted.size() - _current_initial_bank < route_signals.size() )
+ {
+ // but don't shift backwards past the zeroth channel
+ switch_banks( max((Sorted::size_type) 0, sorted.size() - route_signals.size() ) );
+ }
+ // Otherwise just refresh the current bank
+ else
+ {
+ refresh_current_bank();
+ }
+}
+
+///////////////////////////////////////////
+// Transport signals
+///////////////////////////////////////////
+
+void MackieControlProtocol::notify_record_state_changed()
+{
+ // switch rec button on / off / flashing
+ Button * rec = reinterpret_cast<Button*>( surface().controls_by_name["record"] );
+ mcu_port().write( builder.build_led( *rec, record_release( *rec ) ) );
+}
+
+void MackieControlProtocol::notify_transport_state_changed()
+{
+ // switch various play and stop buttons on / off
+ update_global_button( "play", session->transport_rolling() );
+ update_global_button( "stop", !session->transport_rolling() );
+ update_global_button( "loop", session->get_play_loop() );
+
+ // rec is special because it's tristate
+ Button * rec = reinterpret_cast<Button*>( surface().controls_by_name["record"] );
+ 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
+/////////////////////////////////////
+LedState MackieControlProtocol::left_press( Button & button )
+{
+ Sorted sorted = get_sorted_routes();
+ if ( sorted.size() > route_table.size() )
+ {
+ int new_initial = _current_initial_bank - route_table.size();
+ if ( new_initial < 0 ) new_initial = 0;
+ if ( new_initial != int( _current_initial_bank ) )
+ {
+ session->set_dirty();
+ switch_banks( new_initial );
+ }
+
+ return on;
+ }
+ else
+ {
+ return flashing;
+ }
+}
+
+LedState MackieControlProtocol::left_release( Button & button )
+{
+ return off;
+}
+
+LedState MackieControlProtocol::right_press( Button & button )
+{
+ Sorted sorted = get_sorted_routes();
+ if ( sorted.size() > route_table.size() )
+ {
+ uint32_t delta = sorted.size() - ( route_table.size() + _current_initial_bank );
+ if ( delta > route_table.size() ) delta = route_table.size();
+ if ( delta > 0 )
+ {
+ session->set_dirty();
+ switch_banks( _current_initial_bank + delta );
+ }
+
+ return on;
+ }
+ else
+ {
+ return flashing;
+ }
+}
+
+LedState MackieControlProtocol::right_release( Button & button )
+{
+ return off;
+}
+
+LedState MackieControlProtocol::channel_left_press( Button & button )
+{
+ Sorted sorted = get_sorted_routes();
+ if ( sorted.size() > route_table.size() )
+ {
+ prev_track();
+ return on;
+ }
+ else
+ {
+ return flashing;
+ }
+}
+
+LedState MackieControlProtocol::channel_left_release( Button & button )
+{
+ return off;
+}
+
+LedState MackieControlProtocol::channel_right_press( Button & button )
+{
+ Sorted sorted = get_sorted_routes();
+ if ( sorted.size() > route_table.size() )
+ {
+ next_track();
+ return on;
+ }
+ else
+ {
+ return flashing;
+ }
+}
+
+LedState MackieControlProtocol::channel_right_release( Button & button )
+{
+ return off;
+}
diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h
new file mode 100644
index 0000000000..d71979b463
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_control_protocol.h
@@ -0,0 +1,307 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef ardour_mackie_control_protocol_h
+#define ardour_mackie_control_protocol_h
+
+#include <vector>
+
+#include <sys/time.h>
+#include <pthread.h>
+
+#include <glibmm/thread.h>
+
+#include <ardour/types.h>
+#include <ardour/session.h>
+#include <midi++/types.h>
+
+#include <control_protocol/control_protocol.h>
+#include "midi_byte_array.h"
+#include "controls.h"
+#include "route_signal.h"
+#include "mackie_button_handler.h"
+#include "mackie_port.h"
+
+namespace MIDI {
+ class Port;
+ class Parser;
+}
+
+namespace Mackie {
+ class Surface;
+}
+
+/**
+ This handles the plugin duties, and the midi encoding and decoding,
+ and the signal callbacks, mostly from ARDOUR::Route.
+
+ The model of the control surface is handled by classes in controls.h
+
+ What happens is that each strip on the control surface has
+ a corresponding route in ControlProtocol::route_table. When
+ an incoming midi message is signaled, the correct route
+ is looked up, and the relevant changes made to it.
+
+ For each route currently in route_table, there's a RouteSignal object
+ which encapsulates the signals that indicate that there are changes
+ to be sent to the surface. The signals are handled by this class.
+
+ Calls to signal handlers pass a Route object which is used to look
+ up the relevant Strip in Surface. Then the state is retrieved from
+ the Route and encoded as the correct midi message.
+*/
+class MackieControlProtocol
+: public ARDOUR::ControlProtocol
+, public Mackie::MackieButtonHandler
+{
+ public:
+ MackieControlProtocol( ARDOUR::Session & );
+ virtual ~MackieControlProtocol();
+
+ int set_active (bool yn);
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ static bool probe();
+
+ Mackie::Surface & surface();
+
+ // control events
+ void handle_control_event( Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state );
+
+ // strip/route related stuff
+ public:
+ /// Signal handler for Route::solo
+ void notify_solo_changed( Mackie::RouteSignal * );
+ /// Signal handler for Route::mute
+ void notify_mute_changed( Mackie::RouteSignal * );
+ /// 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 * );
+ /// Signal handler for Route::name_change
+ void notify_name_changed( void *, Mackie::RouteSignal * );
+ /// Signal handler from Panner::Change
+ void notify_panner_changed( Mackie::RouteSignal * );
+ /// Signal handler for new routes added
+ void notify_route_added( ARDOUR::Session::RouteList & );
+
+ void notify_remote_id_changed();
+
+ /// rebuild the current bank. Called on route added/removed and
+ /// remote id changed.
+ void refresh_current_bank();
+
+ // global buttons (ie button not part of strips)
+ public:
+ // button-related signals
+ void notify_record_state_changed();
+ void notify_transport_state_changed();
+ // mainly to pick up punch-in and punch-out
+ void notify_parameter_changed( const char * );
+ void notify_solo_active_changed( bool );
+
+ // this is called to generate the midi to send in response to
+ // a button press.
+ void update_led( Mackie::Button & button, Mackie::LedState );
+
+ // calls update_led, but looks up the button by name
+ void update_global_button( const std::string & name, Mackie::LedState );
+
+ // transport button handler methods from MackieButtonHandler
+ virtual Mackie::LedState rewind_press( Mackie::Button & );
+ virtual Mackie::LedState rewind_release( Mackie::Button & );
+
+ virtual Mackie::LedState ffwd_press( Mackie::Button & );
+ virtual Mackie::LedState ffwd_release( Mackie::Button & );
+
+ virtual Mackie::LedState stop_press( Mackie::Button & );
+ virtual Mackie::LedState stop_release( Mackie::Button & );
+
+ virtual Mackie::LedState play_press( Mackie::Button & );
+ virtual Mackie::LedState play_release( Mackie::Button & );
+
+ virtual Mackie::LedState record_press( Mackie::Button & );
+ virtual Mackie::LedState record_release( Mackie::Button & );
+
+ virtual Mackie::LedState loop_press( Mackie::Button & );
+ virtual Mackie::LedState loop_release( Mackie::Button & );
+
+ virtual Mackie::LedState punch_in_press( Mackie::Button & );
+ virtual Mackie::LedState punch_in_release( Mackie::Button & );
+
+ virtual Mackie::LedState punch_out_press( Mackie::Button & );
+ virtual Mackie::LedState punch_out_release( Mackie::Button & );
+
+ virtual Mackie::LedState home_press( Mackie::Button & );
+ virtual Mackie::LedState home_release( Mackie::Button & );
+
+ virtual Mackie::LedState end_press( Mackie::Button & );
+ virtual Mackie::LedState end_release( Mackie::Button & );
+
+ // bank switching button handler methods from MackieButtonHandler
+ virtual Mackie::LedState left_press( Mackie::Button & );
+ virtual Mackie::LedState left_release( Mackie::Button & );
+
+ virtual Mackie::LedState right_press( Mackie::Button & );
+ virtual Mackie::LedState right_release( Mackie::Button & );
+
+ virtual Mackie::LedState channel_left_press( Mackie::Button & );
+ virtual Mackie::LedState channel_left_release( Mackie::Button & );
+
+ virtual Mackie::LedState channel_right_press( Mackie::Button & );
+ virtual Mackie::LedState channel_right_release( Mackie::Button & );
+
+ virtual Mackie::LedState clicking_press( Mackie::Button & );
+ virtual Mackie::LedState clicking_release( Mackie::Button & );
+
+ virtual Mackie::LedState global_solo_press( Mackie::Button & );
+ virtual Mackie::LedState global_solo_release( Mackie::Button & );
+
+ protected:
+ // create instances of MackiePort, depending on what's found in ardour.rc
+ void create_ports();
+
+ // shut down the surface
+ void close();
+
+ // create the Surface object, with the correct number
+ // of strips for the currently connected ports and
+ // hook up the control event notification
+ void initialize_surface();
+
+ // This sets up the notifications and sets the
+ // controls to the correct values
+ void update_surface();
+
+ // connects global (not strip) signals from the Session to here
+ // so the surface can be notified of changes from the other UIs.
+ void connect_session_signals();
+
+ // set all controls to their zero position
+ void zero_all();
+
+ /**
+ Fetch the set of routes to be considered for control by the
+ surface. Excluding master, hidden and control routes, and inactive routes
+ */
+ typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted;
+ Sorted get_sorted_routes();
+
+ // bank switching
+ void switch_banks( int initial );
+ void prev_track();
+ void next_track();
+
+ // delete all RouteSignal objects connecting Routes to Strips
+ void clear_route_signals();
+
+ /// This is the main MCU port, ie not an extender port
+ const Mackie::MackiePort & mcu_port() const;
+ Mackie::MackiePort & mcu_port();
+
+ typedef std::vector<Mackie::RouteSignal*> RouteSignals;
+ RouteSignals route_signals;
+
+ // return which of the ports a particular route_table
+ // index belongs to
+ Mackie::MackiePort & port_for_id( uint32_t index );
+
+ /**
+ Handle a button press for the control and return whether
+ the corresponding light should be on or off.
+ */
+ bool handle_strip_button( Mackie::Control &, Mackie::ButtonState, boost::shared_ptr<ARDOUR::Route> );
+
+ /// thread started. Calls monitor_work.
+ static void* _monitor_work (void* arg);
+
+ /// Polling midi port(s) for incoming messages
+ void* monitor_work ();
+
+ /// rebuild the set of ports for this surface
+ void update_ports();
+
+ /// Returns true if there is pending data, false otherwise
+ bool poll_ports();
+
+ /// Trigger the MIDI::Parser
+ void read_ports();
+
+ void add_port( MIDI::Port &, int number );
+
+ /// read automation data from the currently active routes and send to surface
+ void poll_automation();
+
+ // called from poll_automation to figure out which automations need to be sent
+ void update_automation( Mackie::RouteSignal & );
+
+ /**
+ notification that the port is about to start it's init sequence.
+ We must make sure that before this exits, the port is being polled
+ for new data.
+ */
+ void handle_port_init( Mackie::SurfacePort * );
+
+ /// notification from a MackiePort that it's now active
+ void handle_port_active( Mackie::SurfacePort * );
+
+ /// notification from a MackiePort that it's now inactive
+ void handle_port_inactive( Mackie::SurfacePort * );
+
+ boost::shared_ptr<ARDOUR::Route> master_route();
+ Mackie::Strip & master_strip();
+
+ private:
+ boost::shared_ptr<Mackie::RouteSignal> master_route_signal;
+
+ static const char * default_port_name;
+
+ /// The Midi port(s) connected to the units
+ typedef vector<Mackie::MackiePort*> MackiePorts;
+ MackiePorts _ports;
+
+ // the thread that polls the ports for incoming midi data
+ pthread_t thread;
+
+ /// The initial remote_id of the currently switched in bank.
+ uint32_t _current_initial_bank;
+
+ /// protects the port list, and polling structures
+ Glib::Mutex update_mutex;
+
+ /// Protects set_active, and allows waiting on the poll thread
+ Glib::Cond update_cond;
+
+ // because sigc::trackable doesn't seem to be working
+ std::vector<sigc::connection> _connections;
+ std::back_insert_iterator<std::vector<sigc::connection> > connections_back;
+
+ /// The representation of the physical controls on the surface.
+ Mackie::Surface * _surface;
+
+ /// If a port is opened or closed, this will be
+ /// true until the port configuration is updated;
+ bool _ports_changed;
+
+ bool _polling;
+ struct pollfd * pfd;
+ int nfds;
+};
+
+#endif // ardour_mackie_control_protocol_h
diff --git a/libs/surfaces/mackie/mackie_control_protocol_poll.cc b/libs/surfaces/mackie/mackie_control_protocol_poll.cc
new file mode 100644
index 0000000000..05681c0c25
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_control_protocol_poll.cc
@@ -0,0 +1,192 @@
+#include "mackie_control_protocol.h"
+
+#include "midi_byte_array.h"
+#include "surface_port.h"
+
+#include <pbd/pthread_utils.h>
+#include <pbd/error.h>
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <midi++/manager.h>
+#include <midi++/port_request.h>
+#include "i18n.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <errno.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+using namespace std;
+using namespace Mackie;
+using namespace PBD;
+
+const char * MackieControlProtocol::default_port_name = "mcu";
+
+bool MackieControlProtocol::probe()
+{
+ return MIDI::Manager::instance()->port( default_port_name ) != 0;
+}
+
+void * MackieControlProtocol::monitor_work()
+{
+ // What does ThreadCreatedWithRequestSize do?
+ PBD::ThreadCreated (pthread_self(), X_("Mackie"));
+
+ pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
+
+ // read from midi ports
+ while ( _polling )
+ {
+ try
+ {
+ if ( poll_ports() )
+ {
+ try { read_ports(); }
+ catch ( exception & e ) {
+ cout << "MackieControlProtocol::poll_ports caught exception: " << e.what() << endl;
+ _ports_changed = true;
+ update_ports();
+ }
+ }
+ // poll for automation data from the routes
+ poll_automation();
+ }
+ catch ( exception & e )
+ {
+ cout << "caught exception in MackieControlProtocol::monitor_work " << e.what() << endl;
+ }
+ }
+
+ // TODO ports and pfd and nfds should be in a separate class
+ delete[] pfd;
+ pfd = 0;
+ nfds = 0;
+
+ return (void*) 0;
+}
+
+void MackieControlProtocol::update_ports()
+{
+ if ( _ports_changed )
+ {
+ Glib::Mutex::Lock ul( update_mutex );
+ // yes, this is a double-test locking paradigm, or whatever it's called
+ // because we don't *always* need to acquire the lock for the first test
+ if ( _ports_changed )
+ {
+ // create new pollfd structures
+ if ( pfd != 0 ) delete[] pfd;
+ // TODO This might be a memory leak. How does thread cancellation cleanup work?
+ pfd = new pollfd[_ports.size()];
+ nfds = 0;
+
+ for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
+ {
+ //cout << "adding port " << (*it)->port().name() << " to pollfd" << endl;
+ pfd[nfds].fd = (*it)->port().selectable();
+ pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
+ ++nfds;
+ }
+ _ports_changed = false;
+ }
+ update_cond.signal();
+ }
+}
+
+void MackieControlProtocol::read_ports()
+{
+ /* now read any data on the ports */
+ Glib::Mutex::Lock lock( update_mutex );
+ for ( int p = 0; p < nfds; ++p )
+ {
+ // this will cause handle_midi_any in the MackiePort to be triggered
+ if ( pfd[p].revents & POLLIN > 0 )
+ {
+ // avoid deadlocking?
+ // doesn't seem to make a difference
+ //lock.release();
+ _ports[p]->read();
+ //lock.acquire();
+ }
+ }
+}
+
+bool MackieControlProtocol::poll_ports()
+{
+ int timeout = 10; // milliseconds
+ int no_ports_sleep = 1000; // milliseconds
+
+ Glib::Mutex::Lock lock( update_mutex );
+ // if there are no ports
+ if ( nfds < 1 )
+ {
+ lock.release();
+ //cout << "poll_ports no ports" << endl;
+ usleep( no_ports_sleep * 1000 );
+ return false;
+ }
+
+ int retval = poll( pfd, nfds, timeout );
+ if ( retval < 0 )
+ {
+ // gdb at work, perhaps
+ if ( errno != EINTR )
+ {
+ error << string_compose(_("Mackie MIDI thread poll failed (%1)"), strerror( errno ) ) << endmsg;
+ }
+ return false;
+ }
+
+ return retval > 0;
+}
+
+void MackieControlProtocol::handle_port_inactive( SurfacePort * port )
+{
+ // port gone away. So stop polling it ASAP
+ {
+ // delete the port instance
+ Glib::Mutex::Lock lock( update_mutex );
+ MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port );
+ if ( it != _ports.end() )
+ {
+ delete *it;
+ _ports.erase( it );
+ }
+ }
+ _ports_changed = true;
+ update_ports();
+
+ // TODO all the rebuilding of surfaces and so on
+}
+
+void MackieControlProtocol::handle_port_active( SurfacePort * port )
+{
+ // no need to re-add port because it was already added
+ // during the init phase. So just update the local surface
+ // representation and send the representation to
+ // all existing ports
+
+ // TODO update bank size
+
+ // TODO rebuild surface, to have new units
+
+ // 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
+ // _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;
+ _ports_changed = true;
+ update_ports();
+}
diff --git a/libs/surfaces/mackie/mackie_midi_builder.cc b/libs/surfaces/mackie/mackie_midi_builder.cc
new file mode 100644
index 0000000000..8ed98a5720
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_midi_builder.cc
@@ -0,0 +1,173 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "mackie_midi_builder.h"
+
+#include <typeinfo>
+#include <sstream>
+#include <iomanip>
+
+#include "controls.h"
+#include "midi_byte_array.h"
+
+using namespace Mackie;
+using namespace std;
+
+MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const ControlState & state )
+{
+ // TODO do an exact calc for 0.50? To allow manually re-centering the port.
+
+ // center on or off
+ MIDI::byte retval = ( state.pos > 0.45 && state.pos < 0.55 ? 1 : 0 ) << 6;
+
+ // mode
+ retval |= ( mode << 4 );
+
+ // value, but only if off hasn't explicitly been set
+ if ( state.led_state != off )
+ retval += ( int(state.pos * 10.0) + 1 ) & 0x0f; // 0b00001111
+
+ return retval;
+}
+
+MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state )
+{
+ return build_led_ring( pot.led_ring(), state );
+}
+
+MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state )
+{
+ // The other way of doing this:
+ // 0x30 + pot/ring number (0-7)
+ //, 0x30 + led_ring.ordinal() - 1
+ return MidiByteArray ( 3
+ // the control type
+ , midi_pot_id
+ // the id
+ , 0x20 + led_ring.id()
+ // the value
+ , calculate_pot_value( midi_pot_mode_dot, state )
+ );
+}
+
+MidiByteArray MackieMidiBuilder::build_led( const Button & button, LedState ls )
+{
+ return build_led( button.led(), ls );
+}
+
+MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
+{
+ MIDI::byte state = 0;
+ switch ( ls.state() )
+ {
+ case LedState::on: state = 0x7f; break;
+ case LedState::off: state = 0x00; break;
+ case LedState::none: state = 0x00; break; // actually, this should never happen.
+ case LedState::flashing: state = 0x01; break;
+ }
+
+ return MidiByteArray ( 3
+ , midi_button_id
+ , led.id()
+ , state
+ );
+}
+
+MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
+{
+ int posi = int( 0x3fff * pos );
+
+ return MidiByteArray ( 3
+ , midi_fader_id | fader.id()
+ // lower-order bits
+ , posi & 0x7f
+ // higher-order bits
+ , ( posi >> 7 )
+ );
+}
+
+MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
+{
+ Group::Controls::const_iterator it = strip.controls().begin();
+ MidiByteArray retval;
+ for (; it != strip.controls().end(); ++it )
+ {
+ Control & control = **it;
+ if ( control.accepts_feedback() )
+ retval << zero_control( control );
+ }
+ return retval;
+}
+
+MidiByteArray MackieMidiBuilder::zero_control( const Control & control )
+{
+ switch( control.type() )
+ {
+ case Control::type_button:
+ return build_led( (Button&)control, off );
+
+ case Control::type_led:
+ return build_led( (Led&)control, off );
+
+ case Control::type_fader:
+ return build_fader( (Fader&)control, 0.0 );
+
+ case Control::type_pot:
+ return build_led_ring( dynamic_cast<const Pot&>( control ), off );
+
+ case Control::type_led_ring:
+ return build_led_ring( dynamic_cast<const LedRing&>( control ), off );
+
+ default:
+ ostringstream os;
+ os << "Unknown control type " << control << " in Strip::zero_control";
+ throw MackieControlException( os.str() );
+ }
+}
+
+char translate_seven_segment( char achar )
+{
+ achar = toupper( achar );
+ if ( achar >= 0x40 && achar <= 0x60 )
+ return achar - 0x40;
+ else if ( achar >= 0x21 && achar <= 0x3f )
+ return achar;
+ else
+ return 0x00;
+}
+
+MidiByteArray MackieMidiBuilder::two_char_display( const std::string & msg, const std::string & dots )
+{
+ if ( msg.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: msg must be exactly 2 characters" );
+ if ( dots.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: dots must be exactly 2 characters" );
+
+ MidiByteArray bytes( 5, 0xb0, 0x4a, 0x00, 0x4b, 0x00 );
+
+ // chars are understood by the surface in right-to-left order
+ // could also exchange the 0x4a and 0x4b, above
+ bytes[4] = translate_seven_segment( msg[0] ) + ( dots[0] == '.' ? 0x40 : 0x00 );
+ bytes[2] = translate_seven_segment( msg[1] ) + ( dots[1] == '.' ? 0x40 : 0x00 );
+
+ return bytes;
+}
+
+MidiByteArray MackieMidiBuilder::two_char_display( unsigned int value, const std::string & dots )
+{
+ ostringstream os;
+ os << setfill('0') << setw(2) << value % 100;
+ return two_char_display( os.str() );
+}
diff --git a/libs/surfaces/mackie/mackie_midi_builder.h b/libs/surfaces/mackie/mackie_midi_builder.h
new file mode 100644
index 0000000000..f0c3d51a54
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_midi_builder.h
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef mackie_midi_builder_h
+#define mackie_midi_builder_h
+
+#include "midi_byte_array.h"
+#include "types.h"
+
+namespace Mackie
+{
+
+/**
+ This knows how to build midi messages given a control and
+ a state.
+*/
+class MackieMidiBuilder
+{
+public:
+ /**
+ The first byte of a midi message from the surface
+ will contain one of these, sometimes bitmasked
+ with the control id
+ */
+ enum midi_types {
+ midi_fader_id = 0xe0
+ , midi_button_id = 0x90
+ , midi_pot_id = 0xb0
+ };
+
+ /**
+ The LED rings have these modes.
+ */
+ enum midi_pot_mode {
+ midi_pot_mode_dot = 0
+ , midi_pot_mode_boost_cut = 1
+ , midi_pot_mode_wrap = 2
+ , midi_pot_mode_spread = 3
+ };
+
+ MidiByteArray build_led_ring( const Pot & pot, const ControlState & );
+ MidiByteArray build_led_ring( const LedRing & led_ring, const ControlState & );
+
+ MidiByteArray build_led( const Led & led, LedState ls );
+ MidiByteArray build_led( const Button & button, LedState ls );
+
+ MidiByteArray build_fader( const Fader & fader, float pos );
+
+ /// return bytes that will reset all controls to their zero positions
+ MidiByteArray zero_strip( const Strip & strip );
+
+ // provide bytes to zero the given control
+ MidiByteArray zero_control( const Control & control );
+
+ // display the first 2 chars of the msg in the 2 char display
+ // . is appended to the previous character, so A.B. would
+ // be two characters
+ MidiByteArray two_char_display( const std::string & msg, const std::string & dots = " " );
+ MidiByteArray two_char_display( unsigned int value, const std::string & dots = " " );
+
+protected:
+ static MIDI::byte calculate_pot_value( midi_pot_mode mode, const ControlState & );
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/mackie_port.cc b/libs/surfaces/mackie/mackie_port.cc
new file mode 100644
index 0000000000..9bcee638fb
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_port.cc
@@ -0,0 +1,399 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "mackie_port.h"
+
+#include "mackie_control_exception.h"
+#include "mackie_control_protocol.h"
+#include "mackie_midi_builder.h"
+#include "controls.h"
+#include "surface.h"
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <sigc++/sigc++.h>
+#include <boost/shared_array.hpp>
+#include <ardour/configuration.h>
+
+#include "i18n.h"
+
+#include <sstream>
+
+using namespace std;
+using namespace Mackie;
+
+// The MCU sysex header
+MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 );
+
+// The MCU extender sysex header
+MidiByteArray mackie_sysex_hdr_xt ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11 );
+
+MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t port_type )
+: SurfacePort( port, number )
+, _mcp( mcp )
+, _port_type( port_type )
+, _emulation( none )
+, _initialising( true )
+{
+ //cout << "MackiePort::MackiePort" <<endl;
+}
+
+MackiePort::~MackiePort()
+{
+ //cout << "~MackiePort" << endl;
+ close();
+ //cout << "~MackiePort finished" << endl;
+}
+
+int MackiePort::strips() const
+{
+ if ( _port_type == mcu )
+ {
+ switch ( _emulation )
+ {
+ // BCF2000 only has 8 faders, so reserve one for master
+ case bcf2000: return 7;
+ case mackie: return 8;
+ case none:
+ default:
+ throw MackieControlException( "MackiePort::strips: don't know what emulation we're using" );
+ }
+ }
+ else
+ {
+ // must be an extender, ie no master fader
+ return 8;
+ }
+}
+
+// should really be in MackiePort
+void MackiePort::open()
+{
+ //cout << "MackiePort::open " << *this << endl;
+ _sysex = port().input()->sysex.connect( ( mem_fun (*this, &MackiePort::handle_midi_sysex) ) );
+
+ // make sure the device is connected
+ init();
+}
+
+void MackiePort::close()
+{
+ //cout << "MackiePort::close" << endl;
+
+ // disconnect signals
+ _any.disconnect();
+ _sysex.disconnect();
+
+ // TODO emit a "closing" signal?
+ //cout << "MackiePort::close finished" << endl;
+}
+
+const MidiByteArray & MackiePort::sysex_hdr() const
+{
+ switch ( _port_type )
+ {
+ case mcu: return mackie_sysex_hdr;
+ case ext: return mackie_sysex_hdr_xt;
+ }
+ cout << "MackiePort::sysex_hdr _port_type not known" << endl;
+ return mackie_sysex_hdr;
+}
+
+Control & MackiePort::lookup_control( const MidiByteArray & bytes )
+{
+ Control * control = 0;
+ int midi_id = -1;
+ MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
+ switch( midi_type )
+ {
+ // fader
+ case MackieMidiBuilder::midi_fader_id:
+ midi_id = bytes[0] & 0x0f;
+ control = _mcp.surface().faders[midi_id];
+ if ( control == 0 )
+ {
+ ostringstream os;
+ os << "control for fader" << midi_id << " is null";
+ throw MackieControlException( os.str() );
+ }
+ break;
+
+ // button
+ case MackieMidiBuilder::midi_button_id:
+ midi_id = bytes[1];
+ control = _mcp.surface().buttons[midi_id];
+ if ( control == 0 )
+ {
+ ostringstream os;
+ os << "control for button" << midi_id << " is null";
+ throw MackieControlException( os.str() );
+ }
+ break;
+
+ // pot (jog wheel, external control)
+ case MackieMidiBuilder::midi_pot_id:
+ midi_id = bytes[1] & 0x1f;
+ control = _mcp.surface().pots[midi_id];
+ if ( control == 0 )
+ {
+ ostringstream os;
+ os << "control for button" << midi_id << " is null";
+ throw MackieControlException( os.str() );
+ }
+ break;
+
+ default:
+ ostringstream os;
+ os << "Cannot find control for " << bytes;
+ throw MackieControlException( os.str() );
+ }
+ return *control;
+}
+
+MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiByteArray::iterator end )
+{
+ MidiByteArray l;
+ back_insert_iterator<MidiByteArray> back ( l );
+ copy( begin, end, back );
+
+ MidiByteArray retval;
+
+ // this is how to calculate the response to the challenge.
+ // from the Logic docs.
+ retval << ( 0x7f & ( l[0] + ( l[1] ^ 0xa ) - l[3] ) );
+ retval << ( 0x7f & ( ( l[2] >> l[3] ) ^ ( l[0] + l[3] ) ) );
+ retval << ( 0x7f & ( l[3] - ( l[2] << 2 ) ^ ( l[0] | l[1] ) ) );
+ retval << ( 0x7f & ( l[1] - l[2] + ( 0xf0 ^ ( l[3] << 4 ) ) ) );
+
+ return retval;
+}
+
+// not used right now
+MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
+{
+ // handle host connection query
+ //cout << "host connection query: " << bytes << endl;
+
+ if ( bytes.size() != 18 )
+ {
+ finalise_init( false );
+ ostringstream os;
+ os << "expecting 18 bytes, read " << bytes << " from " << port().name();
+ throw MackieControlException( os.str() );
+ }
+
+ // build and send host connection reply
+ MidiByteArray response;
+ response << 0x02;
+ copy( bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter( response ) );
+ response << calculate_challenge_response( bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4 );
+ return response;
+}
+
+// not used right now
+MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes )
+{
+ //cout << "host_connection_confirmation: " << bytes << endl;
+
+ // decode host connection confirmation
+ if ( bytes.size() != 14 )
+ {
+ finalise_init( false );
+ ostringstream os;
+ os << "expecting 14 bytes, read " << bytes << " from " << port().name();
+ throw MackieControlException( os.str() );
+ }
+
+ // send version request
+ return MidiByteArray( 2, 0x13, 0x00 );
+}
+
+void MackiePort::probe_emulation( const MidiByteArray & bytes )
+{
+ //cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl;
+ string version_string;
+ for ( int i = 6; i < 11; ++i ) version_string.append( 1, (char)bytes[i] );
+ //cout << "version_string: " << version_string << endl;
+
+ // TODO investigate using serial number. Also, possibly size of bytes might
+ // give an indication. Also, apparently MCU sends non-documented messages
+ // sometimes.
+ if (!_initialising)
+ {
+ cout << "MackiePort::probe_emulation out of sequence." << endl;
+ return;
+ }
+
+ finalise_init( true );
+}
+
+void MackiePort::init()
+{
+ //cout << "MackiePort::init" << endl;
+ init_mutex.lock();
+ _initialising = true;
+
+ //cout << "MackiePort::lock acquired" << endl;
+ // emit pre-init signal
+ init_event();
+
+ // kick off initialisation. See docs in header file for init()
+
+ // bypass the init sequence because sometimes the first
+ // message doesn't get to the unit, and there's no way
+ // to do a timed lock in Glib.
+ //write_sysex ( MidiByteArray ( 2, 0x13, 0x00 ) );
+
+ finalise_init( true );
+}
+
+void MackiePort::finalise_init( bool yn )
+{
+ //cout << "MackiePort::finalise_init" << endl;
+ bool emulation_ok = false;
+
+ // probing doesn't work very well, so just use a config variable
+ // to set the emulation mode
+ if ( _emulation == none )
+ {
+ if ( ARDOUR::Config->get_mackie_emulation() == "bcf" )
+ {
+ _emulation = bcf2000;
+ emulation_ok = true;
+ }
+ else if ( ARDOUR::Config->get_mackie_emulation() == "mcu" )
+ {
+ _emulation = mackie;
+ emulation_ok = true;
+ }
+ else
+ {
+ cout << "unknown mackie emulation: " << ARDOUR::Config->get_mackie_emulation() << endl;
+ emulation_ok = false;
+ }
+ }
+
+ yn = yn && emulation_ok;
+
+ SurfacePort::active( yn );
+
+ if ( yn )
+ {
+ active_event();
+
+ // start handling messages from controls
+ _any = port().input()->any.connect( ( mem_fun (*this, &MackiePort::handle_midi_any) ) );
+ }
+ _initialising = false;
+ init_cond.signal();
+ init_mutex.unlock();
+}
+
+bool MackiePort::wait_for_init()
+{
+ Glib::Mutex::Lock lock( init_mutex );
+ while ( _initialising )
+ {
+ //cout << "MackiePort::wait_for_active waiting" << endl;
+ init_cond.wait( init_mutex );
+ //cout << "MackiePort::wait_for_active released" << endl;
+ }
+ //cout << "MackiePort::wait_for_active returning" << endl;
+ return SurfacePort::active();
+}
+
+void MackiePort::handle_midi_sysex (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
+{
+ MidiByteArray bytes( count, raw_bytes );
+ //cout << "handle_midi_sysex: " << bytes << endl;
+ switch( bytes[5] )
+ {
+ case 0x01:
+ // not used right now
+ write_sysex( host_connection_query( bytes ) );
+ break;
+ case 0x03:
+ // not used right now
+ write_sysex( host_connection_confirmation( bytes ) );
+ break;
+ case 0x04:
+ inactive_event();
+ cout << "host connection error" << bytes << endl;
+ break;
+ case 0x14:
+ probe_emulation( bytes );
+ break;
+ default:
+ cout << "unknown sysex: " << bytes << endl;
+ }
+}
+
+// converts midi messages into control_event signals
+void MackiePort::handle_midi_any (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
+{
+ MidiByteArray bytes( count, raw_bytes );
+ try
+ {
+ // ignore sysex messages
+ if ( bytes[0] == MIDI::sysex ) return;
+
+ Control & control = lookup_control( bytes );
+
+ // This handles incoming bytes. Outgoing bytes
+ // are sent by the signal handlers.
+ switch ( control.type() )
+ {
+ // fader
+ case Control::type_fader:
+ {
+ // for a BCF2000, max is 7f for high-order byte and 0x70 for low-order byte
+ // According to the Logic docs, these should both be 0x7f.
+ // Although it does mention something about only the top-order
+ // 10 bits out of 14 being used
+ int midi_pos = ( bytes[2] << 7 ) + bytes[1];
+ control_event( *this, control, float(midi_pos) / float(0x3fff) );
+ }
+ break;
+
+ // button
+ case Control::type_button:
+ control_event( *this, control, bytes[2] == 0x7f ? press : release );
+ break;
+
+ // pot (jog wheel, external control)
+ case Control::type_pot:
+ {
+ ControlState state;
+
+ // bytes[2] & 0b01000000 (0x40) give sign
+ int sign = ( bytes[2] & 0x40 ) == 0 ? 1 : -1;
+ // bytes[2] & 0b00111111 (0x3f) gives delta
+ state.ticks = ( bytes[2] & 0x3f) * sign;
+ state.delta = float( state.ticks ) / float( 0x3f );
+
+ control_event( *this, control, state );
+ }
+ break;
+ default:
+ cerr << "Do not understand control type " << control;
+ }
+ }
+ catch( MackieControlException & e )
+ {
+ //cout << bytes << ' ' << e.what() << endl;
+ }
+}
diff --git a/libs/surfaces/mackie/mackie_port.h b/libs/surfaces/mackie/mackie_port.h
new file mode 100644
index 0000000000..2ad5cf6154
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_port.h
@@ -0,0 +1,122 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef mackie_port_h
+#define mackie_port_h
+
+#include "surface_port.h"
+
+#include <midi++/types.h>
+#include <sigc++/signal.h>
+#include <sigc++/connection.h>
+
+#include <glibmm/thread.h>
+
+#include "midi_byte_array.h"
+#include "types.h"
+
+namespace MIDI {
+ class Port;
+ class Parser;
+}
+
+class MackieControlProtocol;
+
+namespace Mackie
+{
+
+class MackiePort : public SurfacePort
+{
+public:
+ enum port_type_t { mcu, ext };
+ enum emulation_t { none, mackie, bcf2000 };
+
+ MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t = mcu );
+ ~MackiePort();
+
+ virtual void open();
+ virtual void close();
+
+ /// MCU and extenders have different sysex headers
+ virtual const MidiByteArray & sysex_hdr() const;
+
+ /// Handle device initialisation
+ void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t );
+
+ /// Handle all control messags
+ void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t );
+
+ Control & lookup_control( const MidiByteArray & bytes );
+
+ /// return the number of strips associated with this port
+ virtual int strips() const;
+
+ /// Block until the port has finished initialising, and then return
+ /// whether the intialisation succeeded
+ bool wait_for_init();
+
+ emulation_t emulation() const { return _emulation; }
+
+protected:
+ /**
+ The initialisation sequence is fairly complex. First a lock is acquired
+ so that a condition can be used to signal the end of the init process.
+ Then a sysex is sent to the device. The response to the sysex
+ is handled by a switch in handle_midi_sysex which calls one of the
+ other methods.
+
+ However, windows DAWs ignore the documented init sequence and so we
+ do too. Thanks to Essox for helping with this.
+
+ So we use the version firmware to figure out what device is on
+ the other end of the cable.
+ */
+ void init();
+
+ /**
+ Once the device is initialised, finalise_init(true) is called, which
+ releases the lock and signals the condition, and starts handling incoming
+ messages. finalise_init(false) will also release the lock but doesn't
+ start handling messages.
+ */
+ void finalise_init( bool yn );
+
+ MidiByteArray host_connection_query( MidiByteArray & bytes );
+ MidiByteArray host_connection_confirmation( const MidiByteArray & bytes );
+
+ /**
+ Will set _emulation to what it thinks is correct, based
+ on responses from the device. Or get/set parameters. Or
+ environment variables. Or existence of a file.
+ */
+ void probe_emulation( const MidiByteArray & bytes );
+
+private:
+ MackieControlProtocol & _mcp;
+ port_type_t _port_type;
+ sigc::connection _any;
+ sigc::connection _sysex;
+ emulation_t _emulation;
+
+ bool _initialising;
+ Glib::Cond init_cond;
+ Glib::Mutex init_mutex;
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/mackie_surface.cc b/libs/surfaces/mackie/mackie_surface.cc
new file mode 100644
index 0000000000..b527f710cc
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_surface.cc
@@ -0,0 +1,1504 @@
+/*
+ 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
+ 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 );
+
+}
+
+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 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 );
+}
diff --git a/libs/surfaces/mackie/mackie_surface.h b/libs/surfaces/mackie/mackie_surface.h
new file mode 100644
index 0000000000..735cbc5851
--- /dev/null
+++ b/libs/surfaces/mackie/mackie_surface.h
@@ -0,0 +1,27 @@
+#ifndef mackie_surface_mackie_h
+#define mackie_surface_mackie_h
+/*
+ Generated by scripts/generate-surface.rb
+*/
+
+#include "surface.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler;
+
+class MackieSurface : public Surface
+{
+public:
+ MackieSurface( uint32_t max_strips ) : Surface( max_strips )
+ {
+ }
+
+ virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
+ virtual void init_controls();
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/midi_byte_array.cc b/libs/surfaces/mackie/midi_byte_array.cc
new file mode 100644
index 0000000000..192af6a1ce
--- /dev/null
+++ b/libs/surfaces/mackie/midi_byte_array.cc
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "midi_byte_array.h"
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <algorithm>
+#include <cstdarg>
+#include <iomanip>
+
+using namespace std;
+
+MidiByteArray::MidiByteArray( size_t size, MIDI::byte array[] )
+: std::vector<MIDI::byte>()
+{
+ for ( size_t i = 0; i < size; ++i )
+ {
+ push_back( array[i] );
+ }
+}
+
+MidiByteArray::MidiByteArray( size_t count, MIDI::byte first, ... )
+: vector<MIDI::byte>()
+{
+ push_back( first );
+ va_list var_args;
+ va_start( var_args, first );
+ for ( size_t i = 1; i < count; ++i )
+ {
+ MIDI::byte b = va_arg( var_args, int );
+ push_back( b );
+ }
+ va_end( var_args );
+}
+
+boost::shared_array<MIDI::byte> MidiByteArray::bytes() const
+{
+ MIDI::byte * buf = new MIDI::byte[size()];
+ const_iterator it = begin();
+ for( MIDI::byte * ptr = buf; it != end(); ++it )
+ {
+ *ptr++ = *it;
+ }
+ return boost::shared_array<MIDI::byte>( buf );
+}
+
+void MidiByteArray::copy( size_t count, MIDI::byte * arr )
+{
+ for( size_t i = 0; i < count; ++i )
+ {
+ push_back( arr[i] );
+ }
+}
+
+MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b )
+{
+ mba.push_back( b );
+ return mba;
+}
+
+MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr )
+{
+ back_insert_iterator<MidiByteArray> bit( mba );
+ copy( barr.begin(), barr.end(), bit );
+ return mba;
+}
+
+ostream & operator << ( ostream & os, const MidiByteArray & mba )
+{
+ os << "[";
+ char fill = os.fill('0');
+ for( MidiByteArray::const_iterator it = mba.begin(); it != mba.end(); ++it )
+ {
+ if ( it != mba.begin() ) os << " ";
+ os << hex << setw(2) << (int)*it;
+ }
+ os.fill( fill );
+ os << dec;
+ os << "]";
+ return os;
+}
diff --git a/libs/surfaces/mackie/midi_byte_array.h b/libs/surfaces/mackie/midi_byte_array.h
new file mode 100644
index 0000000000..e77ece1b88
--- /dev/null
+++ b/libs/surfaces/mackie/midi_byte_array.h
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef midi_byte_array_h
+#define midi_byte_array_h
+
+#include <iostream>
+#include <vector>
+
+#include <boost/shared_array.hpp>
+
+//#include <midi++/types.h>
+namespace MIDI {
+ typedef unsigned char byte;
+}
+
+/**
+ To make building arrays of bytes easier. Thusly:
+
+ MidiByteArray mba;
+ mba << 0xf0 << 0x00 << 0xf7;
+
+ MidiByteArray buf;
+ buf << mba;
+
+ MidiByteArray direct( 3, 0xf0, 0x00, 0xf7 );
+
+ cout << mba << endl;
+ cout << buf << endl;
+ cout << direct << endl;
+
+ will all result in "f0 00 f7" being output to stdout
+*/
+class MidiByteArray : public std::vector<MIDI::byte>
+{
+public:
+ MidiByteArray() : std::vector<MIDI::byte>() {}
+
+ MidiByteArray( size_t count, MIDI::byte array[] );
+
+ /**
+ Accepts a preceding count, and then a list of bytes
+ */
+ MidiByteArray( size_t count, MIDI::byte first, ... );
+
+ /// return smart pointer to a copy of the bytes
+ boost::shared_array<MIDI::byte> bytes() const;
+
+ /// copy the given number of bytes from the given array
+ void copy( size_t count, MIDI::byte arr[] );
+};
+
+/// append the given byte to the end of the array
+MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b );
+
+/// append the given array to the end of this array
+MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr );
+
+/// output the bytes as hex to the given stream
+std::ostream & operator << ( std::ostream & os, const MidiByteArray & mba );
+
+#endif
diff --git a/libs/surfaces/mackie/route_signal.cc b/libs/surfaces/mackie/route_signal.cc
new file mode 100644
index 0000000000..d77d0520a1
--- /dev/null
+++ b/libs/surfaces/mackie/route_signal.cc
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "route_signal.h"
+
+#include <ardour/route.h>
+#include <ardour/track.h>
+#include <ardour/panner.h>
+
+#include "mackie_control_protocol.h"
+
+#include <stdexcept>
+
+using namespace Mackie;
+
+void RouteSignal::connect()
+{
+ if ( _strip.has_solo() )
+ _solo_changed_connection = _route.solo_control().Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_solo_changed ), this ) );
+
+ if ( _strip.has_mute() )
+ _mute_changed_connection = _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 ) );
+
+ _name_changed_connection = _route.name_changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), this ) );
+
+ if ( _route.panner().size() == 1 )
+ {
+ _panner_changed_connection = _route.panner()[0]->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this ) );
+ }
+
+ 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 ) )
+ ;
+ }
+ catch ( std::bad_cast & )
+ {
+ // this should catch the dynamic_cast to Track, if what we're working
+ // with can't be record-enabled
+ }
+
+ // TODO
+ // active_changed
+ // SelectedChanged
+ // RemoteControlIDChanged. 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();
+}
+
+void RouteSignal::notify_all()
+{
+ if ( _strip.has_solo() )
+ _mcp.notify_solo_changed( this );
+
+ if ( _strip.has_mute() )
+ _mcp.notify_mute_changed( this );
+
+ if ( _strip.has_gain() )
+ _mcp.notify_gain_changed( this );
+
+ _mcp.notify_name_changed( &_route, this );
+
+ if ( _strip.has_vpot() )
+ _mcp.notify_panner_changed( this );
+
+ if ( _strip.has_recenable() )
+ _mcp.notify_record_enable_changed( this );
+}
diff --git a/libs/surfaces/mackie/route_signal.h b/libs/surfaces/mackie/route_signal.h
new file mode 100644
index 0000000000..0239980fd4
--- /dev/null
+++ b/libs/surfaces/mackie/route_signal.h
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef route_signal_h
+#define route_signal_h
+
+#include <sigc++/sigc++.h>
+
+class MackieControlProtocol;
+
+namespace ARDOUR {
+ class Route;
+}
+
+namespace Mackie
+{
+
+class Strip;
+class MackiePort;
+
+/**
+ This class is intended to easily create and destroy the set of
+ connections from a route to a control surface strip. Instantiating
+ it will connect the signals, and destructing it will disconnect
+ the signals.
+*/
+class RouteSignal
+{
+public:
+ RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip, MackiePort & port )
+ : _route( route ), _mcp( mcp ), _strip( strip ), _port( port )
+ {
+ connect();
+ }
+
+ ~RouteSignal()
+ {
+ disconnect();
+ }
+
+ void connect();
+ void disconnect();
+
+ // call all signal handlers manually
+ void notify_all();
+
+ const ARDOUR::Route & route() const { return _route; }
+ Strip & strip() { return _strip; }
+ MackiePort & port() { return _port; }
+
+private:
+ ARDOUR::Route & _route;
+ MackieControlProtocol & _mcp;
+ Strip & _strip;
+ MackiePort & _port;
+
+ sigc::connection _solo_changed_connection;
+ sigc::connection _mute_changed_connection;
+ sigc::connection _record_enable_changed_connection;
+ sigc::connection _gain_changed_connection;
+ sigc::connection _name_changed_connection;
+ sigc::connection _panner_changed_connection;
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/scripts/bank.rb b/libs/surfaces/mackie/scripts/bank.rb
new file mode 100644
index 0000000000..e1482545ee
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/bank.rb
@@ -0,0 +1,32 @@
+#! /usr/bin/ruby
+
+class Bank
+ attr_accessor :routes, :strips, :current
+
+ def initialize( routes = 17, strips = 8, current = 0 )
+ @routes = routes
+ @strips = strips
+ @current = current
+ end
+
+ def left
+ new_initial = current - routes
+ if new_initial < 0
+ new_initial = 0
+ end
+ current = new_initial
+ self
+ end
+
+ def right
+ delta = routes - ( strips + current ) - 1
+ puts "delta: #{delta}"
+ if delta > strips
+ delta = strips
+ end
+ @current += delta
+ self
+ end
+end
+
+b=Bank.new
diff --git a/libs/surfaces/mackie/scripts/bcf-controls.csv b/libs/surfaces/mackie/scripts/bcf-controls.csv
new file mode 100644
index 0000000000..6a6d66f6ac
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/bcf-controls.csv
@@ -0,0 +1,96 @@
+type,count,group,name,switch,led,id
+# faders
+fader,7,strip,gain,1,0,0x00
+fader,1,master,gain,1,0,0x07
+
+# pots
+pot,7,strip,vpot,1,1,0x10
+pot,1,,jog,1,0,0x17
+pot,1,,external,1,0,0x2e
+
+# strip buttons
+button,7,strip,recenable,1,1,0x18
+button,7,strip,solo,1,1,0x20
+button,7,strip,mute,1,1,0x10
+button,7,strip,select,1,1,0x0
+button,7,strip,vselect,1,0,0x08
+
+# overlay buttons
+button,1,assignment,io,1,1,0x28
+button,1,assignment,sends,1,1,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,bank,left,1,0,0x2e
+button,1,bank,right,1,0,0x2f
+button,1,bank,channel_left,1,0,0x30
+button,1,bank,channel_right,1,0,0x31
+button,1,,flip,1,1,0x32
+button,1,,edit,1,1,0x56
+
+button,1,display,name_value,1,0,0x34
+button,1,display,smpte_beats,1,0,0x35
+button,1,,F1,1,0,0x36
+button,1,,F2,1,0,0x37
+button,1,,F3,1,0,0x38
+button,1,,F4,1,0,0x39
+button,1,,F5,1,0,0x3a
+button,1,,F6,1,0,0x3b
+button,1,,F7,1,0,0x3c
+button,1,,F8,1,0,0x3d
+button,1,,F9,1,0,0x3e
+button,1,,F10,1,0,0x3f
+button,1,,F11,1,0,0x40
+button,1,,F12,1,0,0x41
+button,1,,F13,1,0,0x42
+button,1,,F14,1,0,0x43
+button,1,,F15,1,0,0x44
+button,1,,F16,1,0,0x45
+# turn on/off all solos
+button,1,,global_solo,1,0,0x27
+button,1,modifiers,option,1,0,0x47
+button,1,modifiers,control,1,0,0x48
+button,1,modifiers,cmd_alt,1,0,0x49
+button,1,automation,on,1,1,0x4a
+button,1,automation,rec_ready,1,1,0x4b
+button,1,functions,undo,1,1,0x4c
+button,1,automation,snapshot,1,1,0x4d
+button,1,automation,touch,1,1,0x4e
+button,1,functions,redo,1,1,0x4f
+button,1,functions,marker,1,1,0x50
+button,1,functions,enter,1,1,0x51
+button,1,functions,cancel,1,0,0x52
+button,1,functions,mixer,1,0,0x53
+button,1,transport,frm_left,1,1,0x54
+button,1,transport,frm_right,1,1,0x55
+button,1,transport,loop,1,1,0x46
+button,1,transport,punch_in,1,1,0x2c
+button,1,transport,punch_out,1,1,0x2b
+button,1,transport,home,1,1,0x2a
+button,1,transport,end,1,1,0x29
+
+# transport buttons
+button,1,transport,"rewind",1,1,0x5b
+button,1,transport,"ffwd",1,1,0x5c
+button,1,transport,"stop",1,1,0x5d
+button,1,transport,"play",1,1,0x5e
+button,1,transport,"record",1,1,0x1f
+button,1,cursor,"cursor_up",1,0,0x60
+button,1,cursor,"cursor_down",1,0,0x61
+button,1,cursor,"cursor_left",1,0,0x62
+button,1,cursor,"cursor_right",1,0,0x63
+button,1,,"zoom",1,1,0x64
+button,1,,"scrub",1,1,0x65
+button,1,user,"user_a",1,0,0x66
+button,1,user,"user_b",1,0,0x67
+
+button,7,strip,"fader_touch",1,0,0x68
+button,1,master,"fader_touch",1,0,0x6f
+button,1,master,mute,1,0,0x17
+button,1,,clicking,1,1,0x33
+
+button,1,,"smpte",0,1,0x71
+button,1,,"beats",0,1,0x72
+button,1,,"solo",0,1,0x73
+button,1,,"relay_click",0,1,0x76
diff --git a/libs/surfaces/mackie/scripts/controls.rb b/libs/surfaces/mackie/scripts/controls.rb
new file mode 100644
index 0000000000..b56fd6010d
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/controls.rb
@@ -0,0 +1,208 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'faster_csv'
+require 'mackie.rb'
+
+class Control
+ attr_accessor :id, :led, :group, :name, :ordinal, :switch
+
+ def initialize( obj, group )
+ @id = obj.id
+ @name = obj.name
+ @ordinal = obj.ordinal
+ @switch = obj.switch
+ @group = group
+ end
+
+ def ordinal_name
+ end
+end
+
+class Fader < Control
+ def self.midi_zero_byte
+ 0xe0
+ end
+
+ def self.mask_for_id( bytes )
+ bytes[0] & 0b00001111
+ end
+end
+
+class Button < Control
+ def self.midi_zero_byte
+ 0x90
+ end
+
+ def self.mask_for_id( bytes )
+ bytes[1]
+ end
+end
+
+class Led < Control
+end
+
+class LedRing < Led
+end
+
+class Pot < Control
+ def self.midi_zero_byte
+ 0xb0
+ end
+
+ def self.mask_for_id( bytes )
+ bytes[1] & 0b00011111
+ end
+
+ def led=( rhs )
+ @led = LedRing.new( rhs, group )
+ end
+end
+
+class Group < Array
+ attr_accessor :name, :controls
+
+ def initialize( name )
+ @name = name
+ end
+
+ def add_control( control )
+ @controls ||= Array.new
+ @controls << control
+ end
+end
+
+class Strip < Group
+
+ attr_accessor :ordinal
+ def initialize( name, ordinal )
+ super( name )
+ @ordinal = ordinal
+ end
+
+ def name
+ @name == 'master' ? @name : "#{@name}_#{@ordinal}"
+ end
+
+ def is_master
+ name == 'master'
+ end
+
+end
+
+types = { 0xe0 => Fader, 0x90 => Button, 0xb0 => Pot }
+
+# number of controls, name, switch, led, id
+# anything that doesn't have the correct number
+# of columns will be ignored
+# actually, 'switch' means it generates data
+# whereas 'led' means it receives data
+
+class Row
+ attr_accessor :count, :name, :switch, :led, :start_id, :type, :group
+ attr_accessor :id, :ordinal_name, :ordinal_group, :ordinal
+
+ def initialize( hash )
+ @count = hash['count'].to_i
+ @name = hash['name']
+ @switch = hash['switch'].to_b
+ @led = hash['led'].to_b
+ @start_id = hash['id'].hex
+ @type = hash['type']
+ @group = hash['group']
+
+ @hash = hash
+ end
+
+ def each_ordinal( &block )
+ for i in 0...count
+ @ordinal = i + 1
+ @ordinal_name = count > 1 ? "#{name}_#{ordinal}" : name
+ @ordinal_group = count > 1 ? "#{group}_#{ordinal}" : group
+ @id = start_id + i
+
+ @hash['ordinal_name'] = @ordinal_name
+ @hash['ordinal_group'] = @ordinal_group
+
+ yield( self )
+ end
+ self
+ end
+
+ def to_hash
+ @hash
+ end
+end
+
+class Surface
+ attr_reader :groups, :controls_by_id, :types, :midis, :controls, :name
+
+ def initialize( name = 'none' )
+ @name = name
+ @types = Hash.new
+ @groups = Hash.new
+ @controls = Array.new
+ @controls_by_id = Hash.new
+ @midis = Hash.new
+ end
+
+ def add_or_create_group( name, ordinal = nil )
+ if name.nil?
+ @groups['none'] = Group.new('none')
+ else
+ group = name =~ /strip/ || name == 'master' ? Strip.new( name, ordinal ) : Group.new( name )
+ @groups[group.name] ||= group
+ end
+ end
+
+ def parse( control_data )
+ FasterCSV.parse( control_data, :headers => true ) do |csv_row|
+ next if csv_row.entries.size < 5 || csv_row[0] =~ /^\s*#/ || csv_row['id'].nil?
+ row = Row.new( csv_row )
+
+ row.each_ordinal do |row|
+ group = add_or_create_group( row.group, row.ordinal )
+ if row.switch
+ # for controls
+ control = eval "#{row.type.capitalize}.new( row, group )"
+
+ # for controls with leds
+ control.led = Led.new( row, group ) if row.led
+ else
+ # for LED-only entries
+ if row.led
+ control = Led.new( row, group )
+ control.led = control
+ end
+ end
+
+ # add the new control to the various lookups
+ @controls_by_id[row.id] = control
+ @controls << control
+ group << control
+
+ # add incoming midi bytes
+ if row.switch
+ types[control.class.midi_zero_byte] = control.class
+ midis[control.class.midi_zero_byte] ||= Hash.new
+ midis[control.class.midi_zero_byte][row.id] = control
+ end
+ end
+ end
+ self
+ end
+end
diff --git a/libs/surfaces/mackie/scripts/dump.rb b/libs/surfaces/mackie/scripts/dump.rb
new file mode 100755
index 0000000000..f1e341fb34
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/dump.rb
@@ -0,0 +1,11 @@
+#! /usr/bin/ruby
+
+while !File.exist? ARGV[0]
+ sleep 0.010
+end
+
+file = File.open ARGV[0], 'r'
+
+while bytes = file.sysread( 3 )
+ puts "%02x %02x %02x" % [ bytes[0], bytes[1], bytes[2] ]
+end
diff --git a/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb b/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb
new file mode 100644
index 0000000000..62bc65d0c3
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/generate-button-handlers-cc.erb
@@ -0,0 +1,59 @@
+<%#
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-%>
+<%-
+require 'controls.rb'
+
+sf = Surface.new
+sf.parse( File.open "mackie-controls.csv" )
+buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
+-%>
+/*
+ Generated by scripts/generate-button-handlers.erb
+*/
+#include "mackie_button_handler.h"
+#include "controls.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace Mackie;
+
+LedState MackieButtonHandler::default_button_press( Button & button )
+{
+ cout << "press: " << button << endl;
+ return on;
+}
+LedState MackieButtonHandler::default_button_release( Button & button )
+{
+ cout << "release: " << button << endl;
+ return off;
+}
+
+<%-
+buttons.each do |button|
+%>
+LedState MackieButtonHandler::<%=button.name%>_press( Button & button )
+{
+ return default_button_press( button );
+}
+
+LedState MackieButtonHandler::<%=button.name%>_release( Button & button )
+{
+ return default_button_release( button );
+}
+<% end %>
diff --git a/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb b/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb
new file mode 100644
index 0000000000..605b6c29dc
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/generate-button-handlers-h.erb
@@ -0,0 +1,54 @@
+<%#
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-%>
+<%-
+require 'controls.rb'
+
+sf = Surface.new
+sf.parse( File.open "mackie-controls.csv" )
+buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
+-%>
+#ifndef mackie_button_handler_h
+#define mackie_button_handler_h
+/*
+ Generated by scripts/generate-button-handlers.erb
+*/
+
+#include "types.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler
+{
+public:
+ virtual ~MackieButtonHandler() {}
+
+ virtual LedState default_button_press( Button & button );
+ virtual LedState default_button_release( Button & button );
+
+ virtual void update_led( Button & button, LedState ls ) = 0;
+
+<%- buttons.each do |button| %>
+ virtual LedState <%=button.name%>_press( Button & );
+ virtual LedState <%=button.name%>_release( Button & );
+<% end %>
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/scripts/generate-surface.rb b/libs/surfaces/mackie/scripts/generate-surface.rb
new file mode 100755
index 0000000000..c6a028804a
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/generate-surface.rb
@@ -0,0 +1,26 @@
+#! /usr/bin/ruby
+
+require 'erb'
+
+require File.dirname(__FILE__) + '/controls.rb'
+
+cc_template = ''
+File.open( File.dirname(__FILE__) + "/surface-cc-template.erb", "r" ) { |f| cc_template = f.read }
+
+h_template = ''
+File.open( File.dirname(__FILE__) + "/surface-h-template.erb", "r" ) { |f| h_template = f.read }
+
+sf = Surface.new( ARGV[0] )
+control_data = ''
+File.open( File.dirname(__FILE__) + "/#{sf.name.downcase}-controls.csv", "r") { |f| control_data = f.read }
+sf.parse control_data
+
+@result = ""
+erb = ERB.new( cc_template , 0, "%<>-", "@result" )
+erb.result
+File.open( "#{sf.name.downcase}_surface.cc", "w" ) { |f| f.write @result }
+
+erb = ERB.new( h_template , 0, "%<>-", "@result" )
+erb.result
+File.open( "#{sf.name.downcase}_surface.h", "w" ) { |f| f.write @result }
+
diff --git a/libs/surfaces/mackie/scripts/host.rb b/libs/surfaces/mackie/scripts/host.rb
new file mode 100755
index 0000000000..8972cba137
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/host.rb
@@ -0,0 +1,133 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'controls.rb'
+require 'mackie.rb'
+
+while !File.exist? ARGV[0]
+ sleep 0.010
+end
+
+#mapping_csv = ARGV[1] || "mackie-controls.csv"
+mapping_csv = ARGV[1]
+puts "mapping_csv: #{mapping_csv}"
+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" )
+
+# all leds off. bcf2000 doesn't respond
+#file.write( hdr + "\x62\xf7" )
+
+# get version. comes back as ASCII bytes
+version = mck.sysex( "\x13\x00" )
+puts "version: #{version.map{|x| x.chr}}"
+
+# write a welcome message. bcf2000 responds with exact
+# string but doesn't display anything
+# 0 offset,
+#~ file.write hdr + "\x12\x3fLCDE\xf7"
+#~ file.flush
+#~ answer = read_sysex file
+#~ puts "answer: #{answer[hdr.length..-1].map{|x| x.chr}}"
+
+# write to BBT display
+#~ file.write hdr + "\x10LCDE\xf7"
+#~ file.flush
+#~ bbt = []
+#~ while ( nc = file.read( 1 ) )[0] != 0xf7
+ #~ bbt << nc[0]
+#~ end
+#~ puts "bbt: #{bbt[hdr.length..-1].map{|x| x.chr}}"
+
+# write 7-segment display
+#~ file.write hdr + "\x11LCDE\xf7"
+#~ file.flush
+
+# go offline. bcf2000 doesn't respond
+#~ file.write( hdr + "\x0f\x7f\xf7" )
+#~ file.flush
+
+sf = Surface.new
+control_data = ""
+File.open( mapping_csv ) { |f| control_data = f.read }
+sf.parse( control_data )
+
+# send all faders to 0, but bounce them first
+# otherwise the bcf gets confused
+sf.midis[0xe0].values.find_all{|x| x.class == Fader}.each do |x|
+ bytes = Array.new
+ bytes[0] = 0xe0 + x.ordinal - 1
+ bytes[1] = 0x1
+ bytes[2] = 0x1
+ file.write bytes.pack( 'C*' )
+ bytes[0] = 0xe0 + x.ordinal - 1
+ bytes[1] = 0x0
+ bytes[2] = 0x0
+ file.write bytes.pack( 'C*' )
+end
+file.flush
+
+# respond to control movements
+while bytes = mck.file.read( 3 )
+ print "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ]
+ midi_type = bytes[0] & 0b11110000
+
+ control_id = sf.types[midi_type].mask_for_id( bytes )
+ control = sf.midis[midi_type][control_id]
+
+ print " Control Type: %-7s, " % sf.types[midi_type]
+ print "id: %4i" % control_id
+ print ", control: %15s" % ( control ? control.name : "nil control" )
+ print ", %15s" % ( control ? control.group.name : "nil group" )
+ print "\n"
+end
diff --git a/libs/surfaces/mackie/scripts/mackie-controls.csv b/libs/surfaces/mackie/scripts/mackie-controls.csv
new file mode 100644
index 0000000000..5dbb6297f8
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/mackie-controls.csv
@@ -0,0 +1,93 @@
+type,count,group,name,switch,led,id
+# faders
+fader,8,strip,gain,1,0,0x00
+fader,1,master,gain,1,0,0x08
+
+# pots
+pot,8,strip,vpot,1,1,0x10
+pot,1,,jog,1,0,0x3c
+pot,1,,external,1,0,0x2e
+
+# strip buttons
+button,8,strip,recenable,1,1,0x0
+button,8,strip,solo,1,1,0x08
+button,8,strip,mute,1,1,0x10
+button,8,strip,select,1,1,0x18
+button,8,strip,vselect,1,0,0x20
+
+# overlay buttons
+button,1,assignment,io,1,1,0x28
+button,1,assignment,sends,1,1,0x29
+button,1,assignment,pan,1,1,0x2a
+button,1,assignment,plugin,1,1,0x2b
+button,1,assignment,eq,1,1,0x2c
+button,1,assignment,dyn,1,1,0x2d
+button,1,bank,left,1,0,0x2e
+button,1,bank,right,1,0,0x2f
+button,1,bank,channel_left,1,0,0x30
+button,1,bank,channel_right,1,0,0x31
+button,1,,flip,1,1,0x32
+button,1,,edit,1,1,0x33
+
+button,1,display,name_value,1,0,0x34
+button,1,display,smpte_beats,1,0,0x35
+button,1,,F1,1,0,0x36
+button,1,,F2,1,0,0x37
+button,1,,F3,1,0,0x38
+button,1,,F4,1,0,0x39
+button,1,,F5,1,0,0x3a
+button,1,,F6,1,0,0x3b
+button,1,,F7,1,0,0x3c
+button,1,,F8,1,0,0x3d
+button,1,,F9,1,0,0x3e
+button,1,,F10,1,0,0x3f
+button,1,,F11,1,0,0x40
+button,1,,F12,1,0,0x41
+button,1,,F13,1,0,0x42
+button,1,,F14,1,0,0x43
+button,1,,F15,1,0,0x44
+button,1,,F16,1,0,0x45
+button,1,modifiers,shift,1,0,0x46
+button,1,modifiers,option,1,0,0x47
+button,1,modifiers,control,1,0,0x48
+button,1,modifiers,cmd_alt,1,0,0x49
+button,1,automation,on,1,1,0x4a
+button,1,automation,rec_ready,1,1,0x4b
+button,1,functions,undo,1,1,0x4c
+button,1,automation,snapshot,1,1,0x4d
+button,1,automation,touch,1,1,0x4e
+button,1,functions,redo,1,1,0x4f
+button,1,functions,marker,1,1,0x50
+button,1,functions,enter,1,1,0x51
+button,1,functions,cancel,1,0,0x52
+button,1,functions,mixer,1,0,0x53
+button,1,transport,frm_left,1,1,0x54
+button,1,transport,frm_right,1,1,0x55
+button,1,transport,loop,1,1,0x56
+button,1,transport,punch_in,1,1,0x57
+button,1,transport,punch_out,1,1,0x58
+button,1,transport,home,1,1,0x59
+button,1,transport,end,1,1,0x5a
+
+# transport buttons
+button,1,transport,"rewind",1,1,0x5b
+button,1,transport,"ffwd",1,1,0x5c
+button,1,transport,"stop",1,1,0x5d
+button,1,transport,"play",1,1,0x5e
+button,1,transport,"record",1,1,0x5f
+button,1,cursor,"cursor_up",1,0,0x60
+button,1,cursor,"cursor_down",1,0,0x61
+button,1,cursor,"cursor_left",1,0,0x62
+button,1,cursor,"cursor_right",1,0,0x63
+button,1,,"zoom",1,1,0x64
+button,1,,"scrub",1,1,0x65
+button,1,user,"user_a",1,0,0x66
+button,1,user,"user_b",1,0,0x67
+
+button,8,strip,"fader_touch",1,0,0x68
+button,1,master,"fader_touch",1,0,0x70
+
+button,1,,"smpte",0,1,0x71
+button,1,,"beats",0,1,0x72
+button,1,,"solo",0,1,0x73
+button,1,,"relay_click",0,1,0x76
diff --git a/libs/surfaces/mackie/scripts/mackie.rb b/libs/surfaces/mackie/scripts/mackie.rb
new file mode 100644
index 0000000000..4c4080ad10
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/mackie.rb
@@ -0,0 +1,119 @@
+class String
+ def to_bytes
+ arr = []
+ each_byte{|x| arr << x}
+ arr
+ end
+end
+
+class Array
+ def to_hex
+ map{|x| "%2.0x" % x}
+ end
+
+ alias as_hex to_hex
+end
+
+class String
+ def to_b
+ to_i != 0 || %w{true t yes y}.include?( self.downcase )
+ end
+end
+
+class Fixnum
+ def to_hex
+ "%02x" % self
+ end
+end
+
+class Mackie
+ attr_accessor :file
+
+ def initialize( file )
+ @file = file
+ end
+
+ # send and receive a sysex message
+ # after wrapping in the header and the eox byte
+ def sysex( msg )
+ puts "Mackie write: #{msg.unpack('C*').to_hex.inspect}"
+ write_sysex( msg )
+ response = read_sysex
+ puts "Mackie response: #{response.to_hex.inspect}"
+ response[5..-1]
+ end
+
+ # returns an array of bytes
+ def read_sysex
+ buf = []
+ while ( nc = @file.read( 1 ) )[0] != 0xf7
+ buf << nc[0]
+ end
+ buf
+ end
+
+ # send and flush a sysex message
+ # after wrapping in the header and the eox byte
+ def write_sysex( msg )
+ @file.write( hdrlc + msg + "\xf7" )
+ @file.flush
+ end
+
+ def write( msg )
+ @file.write msg
+ @file.flush
+ end
+
+ def translate_seven_segment( char )
+ case char
+ when 0x40..0x60
+ char - 0x40
+ when 0x21..0x3f
+ char
+ else
+ 0x00
+ end
+ end
+
+ # display the msg (which can be only 2 characters)
+ # append the number of stops. Options are '..', '. ', '. ', ' '
+ def two_char( msg, stops = ' ' )
+ two = Array.new
+ two << translate_seven_segment( msg.upcase[0] )
+ two << translate_seven_segment( msg.upcase[1] )
+
+ two[0] += 0x40 if stops[0] == '.'[0]
+ two[1] += 0x40 if stops[1] == '.'[0]
+
+ midi_msg = [0xb0, 0x4a, two[1], 0x4b, two[0] ]
+ write midi_msg.pack( 'C*' )
+ end
+
+ # send and receive the device initialisation
+ def init
+ response = sysex( "\x00" )
+
+ # decode host connection query
+ status = response[0]
+ raise( "expected 01, got " + response.inspect ) if status != 1
+
+ serial = response[1..7]
+ challenge = response[8..11]
+
+ # send host connection reply
+ reply = "\x02" + serial.pack('C*') + challenge.pack('C*')
+ response = sysex reply
+
+ # decode host connection confirmation
+ status = response[0]
+ raise ( "expected 03, got " + response.inspect ) if status != 3
+ end
+
+ def hdrlc
+ "\xf0\x00\x00\x66\x10"
+ end
+
+ def hdrlcxt
+ "\xf0\x00\x00\x66\x11"
+ end
+end
diff --git a/libs/surfaces/mackie/scripts/parse.rb b/libs/surfaces/mackie/scripts/parse.rb
new file mode 100644
index 0000000000..3a225a5756
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/parse.rb
@@ -0,0 +1,61 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require "rexml/document"
+file = File.new( ARGV[0] )
+doc = REXML::Document.new file
+
+# fetch the node containing the controls
+controls = XPath.first( doc, 'Session/ControlProtocols/Protocol[@name="Generic MIDI"]/controls' )
+
+channel = 1
+
+# A Control is a button or slider. It has an internal ID
+# an incoming MIDI message, and an outgoing midi message
+class Control
+
+end
+
+# Strips have solo,rec,mute,pan,fader
+# Strips have midi input
+# Strips have midi output
+# Strips have an XML representation, or something like that
+class Strip
+ def initialize( node )
+ @solo = node.elements['solo']
+ @mute = node.elements['mute']
+ @rec = node.elements['recenable']
+ @fader = node.elements['IO/gaincontrol']
+ @panner = node.elements['IO/Panner/StreamPanner/panner']
+ end
+end
+
+# This knows how to extract a set of controls from a Route
+
+doc.elements.each( 'Session/Routes/Route' ) do |node|
+ strip = Strip.new( node )
+
+ controls.add_element( 'mute',
+ 'id' => mute.attribute('id').value,
+ 'event' => "0xb0",
+ 'channel' => channel.to_s,
+ 'additional' => "0x41"
+ )
+
+end
+
+pp controls.elements
diff --git a/libs/surfaces/mackie/scripts/signals.rb b/libs/surfaces/mackie/scripts/signals.rb
new file mode 100644
index 0000000000..8182e562a3
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/signals.rb
@@ -0,0 +1,137 @@
+#~ /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'erb'
+
+signals = %w{
+solo_changed
+mute_changed
+record_enable_changed
+gain_changed
+name_changed
+panner_changed
+}
+
+@signal_calls = { 'panner_changed' => 'panner()[0]->Changed' }
+
+def connection_call( x )
+ if @signal_calls.include? x
+ @signal_calls[x]
+ else
+ x
+ end
+end
+
+signals.each do |x|
+ puts <<EOF
+void MackieControlProtocol::notify_#{x}( void *, ARDOUR::Route * route )
+{
+ try
+ {
+ strip_from_route( route ).#{x.gsub( /_changed/, '' )}();
+ }
+ catch( exception & e )
+ {
+ cout << e.what() << endl;
+ }
+}
+
+EOF
+end
+
+class_def = <<EOF
+#ifndef route_signal_h
+#define route_signal_h
+
+#include <sigc++/sigc++.h>
+
+class MackieControlProtocol;
+
+namespace ARDOUR {
+ class Route;
+}
+
+namespace Mackie
+{
+
+class Strip;
+
+/**
+ This class is intended to easily create and destroy the set of
+ connections from a route to a control surface strip. Instanting
+ it will connect the signals, and destructing it will disconnect
+ the signals.
+*/
+class RouteSignal
+{
+public:
+ RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip )
+ : _route( route ), _mcp( mcp ), _strip( strip )
+ {
+ connect();
+ }
+
+ ~RouteSignal()
+ {
+ disconnect();
+ }
+
+private:
+ ARDOUR::Route & _route;
+ MackieControlProtocol & _mcp;
+ Strip & _strip;
+
+<% signals.each do |x| -%>
+ sigc::connection _<%= x %>_connection;
+<% end -%>
+};
+
+}
+
+#endif
+EOF
+
+erb = ERB.new( class_def, 0, ">-" )
+erb.run
+
+impl_def = <<EOF
+#include "route_signal.h"
+
+#include <ardour/route.h>
+#include <ardour/panner.h>
+
+#include "mackie_control_protocol.h"
+
+using namespace Mackie;
+
+void RouteSignal::connect()
+{
+<% signals.each do |x| -%>
+ _<%=x%>_connection = _route.<%=connection_call(x)%>.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_<%=x%> ), &_route ) );
+<% end -%>
+}
+
+void RouteSignal::disconnect()
+{
+<% signals.each do |x| -%>
+ _<%= x %>_connection.disconnect();
+<% end -%>
+}
+EOF
+
+erb = ERB.new( impl_def, 0, ">-" )
+erb.run
diff --git a/libs/surfaces/mackie/scripts/simple_host.rb b/libs/surfaces/mackie/scripts/simple_host.rb
new file mode 100644
index 0000000000..a5c07f2abb
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/simple_host.rb
@@ -0,0 +1,137 @@
+#! /usr/bin/ruby
+# Copyright (C) 2006,2007 John Anderson
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+require 'mackie'
+
+buttons = {}
+pots = {}
+
+while !File.exist? ARGV[0]
+ sleep 0.010
+end
+
+file = File.open( ARGV[0], 'r+' )
+mck = Mackie.new( file )
+
+# faders to minimum. bcf2000 doesn't respond
+mck.write_sysex "\x61"
+
+# all leds off. bcf2000 doesn't respond
+mck.write_sysex "\x62"
+
+# get version. comes back as ASCII bytes
+version = mck.sysex "\x13\x00"
+puts "version: #{version.map{|x| x.chr}}"
+
+# respond to control movements
+while bytes = file.read( 3 )
+ puts "received: %02.x %02.x %02.x" % [ bytes[0], bytes[1], bytes[2] ]
+ output = nil
+ case bytes[0] & 0b11110000
+ when 0xe0
+ # fader moved, so respond if move is OK
+ output = bytes
+ when 0x90
+ # button pressed
+ case bytes[1]
+ when 0x68..0x6f
+ # do nothing - touch detection
+ puts "touch detect: %02.x" % bytes[2]
+ else
+ # treat all buttons as toggles
+ button_id = bytes[1]
+
+ # only toggle on release. Not working. All buttons send press
+ # and then release signals
+ if bytes[2] == 0
+ if buttons.include?( button_id )
+ # toggle button state
+ puts "button id #{buttons[button_id]} to #{!buttons[button_id]}"
+ buttons[button_id] = !buttons[button_id]
+ else
+ # create a new button as on
+ puts "adding button id #{button_id}"
+ buttons[button_id] = true
+ end
+ bytes[2] = buttons[button_id] ? 0x7f : 0
+ output = bytes
+ end
+ end
+ when 0xb0
+ # pots, jog wheel, external
+ case bytes[1]
+ when 0x10..0x17
+ #pot turned
+ pot_id = bytes[1] & 0b00000111
+ direction = bytes[2] & 0b01000000
+ delta = bytes[2] & 0b00111111
+ sign = direction == 0 ? 1 : -1
+
+ if pots.include? pot_id
+ current_led_pos = pots[pot_id]
+ else
+ current_led_pos = pots[pot_id] = 6
+ end
+ new_led_pos = current_led_pos + sign
+ new_led_pos = case
+ when new_led_pos <= 0
+ 0
+ when new_led_pos >= 11
+ 11
+ else
+ new_led_pos
+ end
+
+ pots[pot_id] = new_led_pos
+
+ puts "pot #{pot_id} turned #{sign} #{direction == 0 ? 'clockwise' : 'widdershins'}: %02.x to #{new_led_pos}" % delta
+
+ output = bytes
+ output[1] += 0x20
+ output[2] = 0b01000000
+ #~ modes:
+ #~ 0 - single dot
+ #~ 1 - boost/cut
+ #~ 2 - wrap
+ #~ 3 - spread
+ mode = pot_id < 4 ? pot_id : 0
+ output[2] |= ( mode << 4 )
+ output[2] += ( new_led_pos ) & 0b00001111
+ when 0x2e
+ # external controller
+ when 0x3c
+ # jog wheel
+ end
+ else
+ puts "don't know what this means"
+ end
+
+ # output bytes
+ if output
+ #sleep 0.1
+ puts "sending: %02.x %02.x %02.x" % [ output[0], output[1], output[2] ]
+ begin
+ res = file.write output
+ puts "res: #{res}"
+ file.flush
+ rescue => e
+ puts "oops #{e}"
+ file.close
+ file = File.open ARGV[0], 'r+'
+ end
+ end
+end
diff --git a/libs/surfaces/mackie/scripts/surface-cc-template.erb b/libs/surfaces/mackie/scripts/surface-cc-template.erb
new file mode 100644
index 0000000000..3b29be3249
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/surface-cc-template.erb
@@ -0,0 +1,95 @@
+<%#
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-%>
+/*
+ Generated by scripts/generate-surface.rb
+*/
+
+#include "<%= sf.name.downcase %>_surface.h"
+
+#include "controls.h"
+#include "mackie_button_handler.h"
+
+using namespace Mackie;
+
+void Mackie::<%= sf.name %>Surface::init_controls()
+{
+ // intialise groups and strips
+ Group * group = 0;
+
+ // make sure there are enough strips
+ strips.resize( <%= sf.groups.values.find_all{|x| x.name =~ /strip/}.size %> );
+
+% sf.groups.values.each do |group|
+ <%- if group.class == Strip -%>
+ <%- if group.name == 'master' -%>
+ group = new MasterStrip ( "<%=group.name%>", 0 );
+ <%- else -%>
+ group = new <%= group.class.name %> ( "<%=group.name%>", <%=group.ordinal - 1%> );
+ <%- end -%>
+ <%- else -%>
+ group = new <%= group.class.name %> ( "<%=group.name%>" );
+ <%- end -%>
+ groups["<%=group.name%>"] = group;
+ <%- if group.class == Strip -%>
+ strips[<%=group.ordinal - 1%>] = dynamic_cast<Strip*>( group );
+ <%- end -%>
+
+% end
+
+ // initialise controls
+ Control * control = 0;
+
+% sf.controls.each do |control|
+ group = groups["<%=control.group.name%>"];
+ control = new <%= control.class.name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group );
+ <%=control.class.name.downcase%>s[0x<%=control.id.to_hex %>] = control;
+ controls.push_back( control );
+ <%- if control.group.class != Strip -%>
+ controls_by_name["<%= control.name %>"] = control;
+ <%- end -%>
+ group->add( *control );
+
+% end
+}
+
+void Mackie::<%= sf.name %>Surface::handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button )
+{
+ if ( bs != press && bs != release )
+ {
+ mbh.update_led( button, none );
+ return;
+ }
+
+ LedState ls;
+ switch ( button.id() )
+ {
+<%-
+buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
+buttons.each do |button|
+%>
+ case 0x<%= button.id.to_hex %>: // <%= button.name %>
+ switch ( bs ) {
+ case press: ls = mbh.<%= button.name %>_press( button ); break;
+ case release: ls = mbh.<%= button.name %>_release( button ); break;
+ case neither: break;
+ }
+ break;
+<% end %>
+ }
+ mbh.update_led( button, ls );
+}
diff --git a/libs/surfaces/mackie/scripts/surface-h-template.erb b/libs/surfaces/mackie/scripts/surface-h-template.erb
new file mode 100644
index 0000000000..2f54f66c47
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/surface-h-template.erb
@@ -0,0 +1,27 @@
+#ifndef mackie_surface_<%= sf.name.downcase %>_h
+#define mackie_surface_<%= sf.name.downcase %>_h
+/*
+ Generated by scripts/generate-surface.rb
+*/
+
+#include "surface.h"
+
+namespace Mackie
+{
+
+class MackieButtonHandler;
+
+class <%= sf.name %>Surface : public Surface
+{
+public:
+ <%= sf.name %>Surface( uint32_t max_strips ) : Surface( max_strips )
+ {
+ }
+
+ virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
+ virtual void init_controls();
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/scripts/test_controls.rb b/libs/surfaces/mackie/scripts/test_controls.rb
new file mode 100755
index 0000000000..782b0d427c
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/test_controls.rb
@@ -0,0 +1,9 @@
+#! /usr/bin/ruby
+
+require 'controls.rb'
+require 'pp'
+
+sf = Surface.new
+sf.parse
+sf.types.each{|k,v| puts "%02.x #{v}" % k}
+
diff --git a/libs/surfaces/mackie/scripts/transform.rb b/libs/surfaces/mackie/scripts/transform.rb
new file mode 100644
index 0000000000..e0221e188b
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/transform.rb
@@ -0,0 +1,26 @@
+class ElementHandler
+
+ def apply( anElement )
+ anElement.each {|e| handle(e)} if anElement
+ end
+
+ def handle( aNode )
+ if aNode.kind_of? REXML::Text
+ handleTextNode(aNode)
+ elsif aNode.kind_of? REXML::Element
+ handle_element aNode
+ else
+ return #ignore comments and processing instructions
+ end
+ end
+
+ def handle_element( anElement )
+ handler_method = "handle_" + anElement.name.tr("-","_")
+ if self.respond_to? handler_method
+ self.send(handler_method, anElement)
+ else
+ default_handler(anElement)
+ end
+ end
+
+end
diff --git a/libs/surfaces/mackie/scripts/write.rb b/libs/surfaces/mackie/scripts/write.rb
new file mode 100644
index 0000000000..20b72477e8
--- /dev/null
+++ b/libs/surfaces/mackie/scripts/write.rb
@@ -0,0 +1,10 @@
+#! /usr/bin/ruby
+
+require 'mackie.rb'
+
+@file = File.open '/dev/snd/midiC2D0', 'r+'
+
+@led_8_on = [ 0x90, 0x18, 0x7f ]
+@hci = [ 0, 0xf7 ]
+@version_req = [ 0x13, 0, 0xf7 ]
+
diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc
new file mode 100644
index 0000000000..01be2c60c2
--- /dev/null
+++ b/libs/surfaces/mackie/surface.cc
@@ -0,0 +1,142 @@
+#include "surface.h"
+
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+
+using namespace std;
+using namespace Mackie;
+
+Surface::Surface( uint32_t max_strips, uint32_t unit_strips )
+: _max_strips( max_strips ), _unit_strips( unit_strips )
+{
+}
+
+void Surface::init()
+{
+ init_controls();
+ init_strips( _max_strips, _unit_strips );
+}
+
+Surface::~Surface()
+{
+ // delete groups
+ for( Groups::iterator it = groups.begin(); it != groups.end(); ++it )
+ {
+ delete it->second;
+ }
+
+ // delete controls
+ for( Controls::iterator it = controls.begin(); it != controls.end(); ++it )
+ {
+ delete *it;
+ }
+}
+
+// Mackie-specific, because of multiple devices on separate ports
+// add the strips from 9..max_strips
+// unit_strips is the number of strips for additional units.
+void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips )
+{
+ if ( strips.size() < max_strips )
+ {
+ strips.resize( max_strips );
+ for ( uint32_t i = strips.size(); i < max_strips; ++i )
+ {
+ // because I can't find itoa
+ ostringstream os;
+ os << "strip_" << i + 1;
+ string name = os.str();
+
+ // shallow copy existing strip
+ // which works because the controls
+ // have the same ids across units
+ Strip * strip = new Strip( *strips[i % unit_strips] );
+
+ // update the relevant values
+ strip->index( i );
+ strip->name( name );
+
+ // add to data structures
+ groups[name] = strip;
+ strips[i] = strip;
+ }
+ }
+}
+
+ostream & Mackie::operator << ( ostream & os, const Mackie::Control & control )
+{
+ os << typeid( control ).name();
+ os << " { ";
+ os << "name: " << control.name();
+ os << ", ";
+ os << "id: " << "0x" << setw(2) << setfill('0') << hex << control.id() << setfill(' ');
+ os << ", ";
+ os << "ordinal: " << dec << control.ordinal();
+ os << ", ";
+ os << "group: " << control.group().name();
+ os << " }";
+
+ return os;
+}
+
+/**
+ TODO could optimise this to use enum, but it's only
+ called during the protocol class instantiation.
+
+ generated using
+
+ irb -r controls.rb
+ sf=Surface.new
+ sf.parse
+ controls = sf.groups.find{|x| x[0] =~ /strip/}.each{|x| puts x[1]}
+ controls[1].each {|x| puts "\telse if ( control.name() == \"#{x.name}\" )\n\t{\n\t\t_#{x.name} = reinterpret_cast<#{x.class.name}*>(&control);\n\t}\n"}
+*/
+void Strip::add( Control & control )
+{
+ Group::add( control );
+ if ( control.name() == "gain" )
+ {
+ _gain = reinterpret_cast<Fader*>(&control);
+ }
+ else if ( control.name() == "vpot" )
+ {
+ _vpot = reinterpret_cast<Pot*>(&control);
+ }
+ else if ( control.name() == "recenable" )
+ {
+ _recenable = reinterpret_cast<Button*>(&control);
+ }
+ else if ( control.name() == "solo" )
+ {
+ _solo = reinterpret_cast<Button*>(&control);
+ }
+ else if ( control.name() == "mute" )
+ {
+ _mute = reinterpret_cast<Button*>(&control);
+ }
+ else if ( control.name() == "select" )
+ {
+ _select = reinterpret_cast<Button*>(&control);
+ }
+ else if ( control.name() == "vselect" )
+ {
+ _vselect = reinterpret_cast<Button*>(&control);
+ }
+ else if ( control.name() == "fader_touch" )
+ {
+ _fader_touch = reinterpret_cast<Button*>(&control);
+ }
+ else if ( control.type() == Control::type_led || control.type() == Control::type_led_ring )
+ {
+ // do nothing
+ cout << "Strip::add not adding " << control << endl;
+ }
+ else
+ {
+ ostringstream os;
+ os << "Strip::add: unknown control type " << control;
+ throw MackieControlException( os.str() );
+ }
+}
+
diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h
new file mode 100644
index 0000000000..0ccde75537
--- /dev/null
+++ b/libs/surfaces/mackie/surface.h
@@ -0,0 +1,94 @@
+#ifndef mackie_surface_h
+#define mackie_surface_h
+
+#include "controls.h"
+#include "types.h"
+#include <stdint.h>
+
+namespace Mackie
+{
+
+class MackieButtonHandler;
+
+/**
+ This represents an entire control surface, made up of Groups,
+ Strips and Controls. There are several collections for
+ ease of addressing in different ways, but only one collection
+ has definitive ownership.
+
+ It handles mapping button ids to press_ and release_ calls.
+
+ There are various emulations of the Mackie around, so specific
+ emulations will inherit from this to change button mapping, or
+ have 7 fader channels instead of 8, or whatever.
+
+ Currently there are BcfSurface and MackieSurface.
+
+ TODO maybe make Group inherit from Control, for ease of ownership.
+*/
+class Surface
+{
+public:
+ /**
+ A Surface can be made up of multiple units. eg one Mackie MCU plus
+ one or more Mackie MCU extenders.
+
+ \param max_strips is the number of strips for the entire surface.
+ \param unit_strips is the number of strips per unit.
+ */
+ Surface( uint32_t max_strips, uint32_t unit_strips = 8 );
+ virtual ~Surface();
+
+ /// Calls the virtual initialisation methods. This *must* be called after
+ /// construction, because c++ is too dumb to call virtual methods from
+ /// inside a constructor
+ void init();
+
+ typedef std::vector<Control*> Controls;
+
+ /// This collection has ownership of all the controls
+ Controls controls;
+
+ /**
+ These are alternative addressing schemes
+ They use maps because the indices aren't always
+ 0-based.
+ */
+ std::map<int,Control*> faders;
+ std::map<int,Control*> pots;
+ std::map<int,Control*> buttons;
+ std::map<int,Control*> leds;
+
+ /// no strip controls in here because they usually
+ /// have the same names.
+ std::map<std::string,Control*> controls_by_name;
+
+ /// The collection of all numbered strips. No master
+ /// strip in here.
+ typedef std::vector<Strip*> Strips;
+ Strips strips;
+
+ /// This collection owns the groups
+ typedef std::map<std::string,Group*> Groups;
+ Groups groups;
+
+ uint32_t max_strips() const
+ {
+ return _max_strips;
+ }
+
+ /// map button ids to calls to press_ and release_ in mbh
+ virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) = 0;
+
+protected:
+ virtual void init_controls() = 0;
+ virtual void init_strips( uint32_t max_strips, uint32_t unit_strips );
+
+private:
+ uint32_t _max_strips;
+ uint32_t _unit_strips;
+};
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc
new file mode 100644
index 0000000000..8aa1be7fe9
--- /dev/null
+++ b/libs/surfaces/mackie/surface_port.cc
@@ -0,0 +1,178 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include "surface_port.h"
+
+#include "mackie_control_exception.h"
+#include "controls.h"
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <sigc++/sigc++.h>
+#include <boost/shared_array.hpp>
+
+#include "i18n.h"
+
+#include <sstream>
+
+#include <cstring>
+#include <cerrno>
+
+using namespace std;
+using namespace Mackie;
+
+SurfacePort::SurfacePort( MIDI::Port & port, int number )
+: _port( port ), _number( number ), _active( false )
+{
+}
+
+SurfacePort::~SurfacePort()
+{
+ //cout << "~SurfacePort::SurfacePort()" << endl;
+ // 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;
+}
+
+// wrapper for one day when strerror_r is working properly
+string fetch_errmsg( int error_number )
+{
+ char * msg = strerror( error_number );
+ return msg;
+}
+
+MidiByteArray SurfacePort::read()
+{
+ const int max_buf_size = 512;
+ MIDI::byte buf[max_buf_size];
+ MidiByteArray retval;
+
+ // check active. Mainly so that the destructor
+ // doesn't destroy the mutex while it's still locked
+ if ( !active() ) return retval;
+
+ // return nothing read if the lock isn't acquired
+ Glib::RecMutex::Lock lock( _rwlock, Glib::TRY_LOCK );
+
+ if ( !lock.locked() )
+ {
+ //cout << "SurfacePort::read not locked" << endl;
+ return retval;
+ }
+
+ // check active again - destructor sequence
+ if ( !active() ) return retval;
+
+ // read port and copy to return value
+ int nread = port().read( buf, sizeof (buf), 0 );
+
+ if (nread >= 0) {
+ retval.copy( nread, buf );
+ if ((size_t) nread == sizeof (buf))
+ {
+ retval << read();
+ }
+ }
+ else
+ {
+ if ( errno != EAGAIN )
+ {
+ ostringstream os;
+ os << "Surface: error reading from port: " << port().name();
+ os << ": " << errno << fetch_errmsg( errno );
+
+ cout << os.str() << endl;
+ inactive_event();
+ throw MackieControlException( os.str() );
+ }
+ }
+ return retval;
+}
+
+void SurfacePort::write( const MidiByteArray & mba )
+{
+ //if ( mba[0] == 0xf0 ) cout << "SurfacePort::write: " << mba << endl;
+ //cout << "SurfacePort::write: " << mba << endl;
+
+ // check active before and after lock - to make sure
+ // that the destructor doesn't destroy the mutex while
+ // it's still in use
+ if ( !active() ) return;
+ Glib::RecMutex::Lock lock( _rwlock );
+ if ( !active() ) return;
+
+ int count = port().write( mba.bytes().get(), mba.size(), 0 );
+ if ( count != (int)mba.size() )
+ {
+ if ( errno != EAGAIN )
+ {
+ ostringstream os;
+ os << "Surface: couldn't write to port " << port().name();
+ os << ": " << errno << fetch_errmsg( errno );
+
+ cout << os.str();
+ inactive_event();
+ throw MackieControlException( os.str() );
+ }
+ }
+ //if ( mba[0] == 0xf0 ) cout << "SurfacePort::write " << count << endl;
+}
+
+void SurfacePort::write_sysex( const MidiByteArray & mba )
+{
+ MidiByteArray buf;
+ buf << sysex_hdr() << mba << MIDI::eox;
+ write( buf );
+}
+
+void SurfacePort::write_sysex( MIDI::byte msg )
+{
+ MidiByteArray buf;
+ buf << sysex_hdr() << msg << MIDI::eox;
+ write( buf );
+}
+
+// This should be moved to midi++ at some point
+ostream & operator << ( ostream & os, const MIDI::Port & port )
+{
+ os << "device: " << port.device();
+ os << "; ";
+ os << "name: " << port.name();
+ os << "; ";
+ os << "type: " << port.type();
+ os << "; ";
+ os << "mode: " << port.mode();
+ os << "; ";
+ os << "ok: " << port.ok();
+ os << "; ";
+ os << "number: " << port.number();
+ os << "; ";
+ return os;
+}
+
+ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
+{
+ os << "{ ";
+ os << "device: " << port.port().device();
+ os << "; ";
+ os << "name: " << port.port().name();
+ os << "; ";
+ os << "number: " << port.number();
+ os << " }";
+ return os;
+}
diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h
new file mode 100644
index 0000000000..87419f1bcd
--- /dev/null
+++ b/libs/surfaces/mackie/surface_port.h
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef surface_port_h
+#define surface_port_h
+
+#include <sigc++/signal.h>
+#include <glibmm/thread.h>
+
+#include "midi_byte_array.h"
+#include "types.h"
+
+namespace MIDI {
+ class Port;
+}
+
+namespace Mackie
+{
+
+/**
+ Make a relationship between a midi port and a Mackie device.
+*/
+class SurfacePort : public sigc::trackable
+{
+public:
+ SurfacePort( MIDI::Port & port, int number );
+ virtual ~SurfacePort();
+
+ // when this is successful, active() should return true
+ virtual void open() = 0;
+
+ // subclasses should call this before doing their own close
+ virtual void close() = 0;
+
+ /// read bytes from the port. They'll either end up in the
+ /// parser, or if that's not active they'll be returned
+ MidiByteArray read();
+
+ /// an easier way to output bytes via midi
+ void write( const MidiByteArray & );
+
+ /// write a sysex message
+ void write_sysex( const MidiByteArray & mba );
+ void write_sysex( MIDI::byte msg );
+
+ // return the correct sysex header for this port
+ virtual const MidiByteArray & sysex_hdr() const = 0;
+
+ MIDI::Port & port() { return _port; }
+ const MIDI::Port & port() const { return _port; }
+
+ // all control notofications are sent from here
+ sigc::signal<void, SurfacePort &, Control &, const ControlState &> control_event;
+
+ // emitted just before the port goes into initialisation
+ // where it tries to establish that its device is connected
+ sigc::signal<void> init_event;
+
+ // emitted when the port completes initialisation successfully
+ sigc::signal<void> active_event;
+
+ // emitted when the port goes inactive (ie a read or write failed)
+ sigc::signal<void> inactive_event;
+
+ // the port number - master is 0, extenders are 1,2,3,4
+ virtual int number() const { return _number; }
+
+ // number of strips handled by this port. Usually 8.
+ virtual int strips() const = 0;
+
+ virtual bool active() const { return _active; }
+ virtual void active( bool yn ) { _active = yn; }
+
+private:
+ MIDI::Port & _port;
+ int _number;
+ bool _active;
+
+ Glib::RecMutex _rwlock;
+};
+
+std::ostream & operator << ( std::ostream & , const SurfacePort & port );
+
+}
+
+#endif
diff --git a/libs/surfaces/mackie/test.cc b/libs/surfaces/mackie/test.cc
new file mode 100644
index 0000000000..351058523f
--- /dev/null
+++ b/libs/surfaces/mackie/test.cc
@@ -0,0 +1,25 @@
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <algorithm>
+#include <cstdarg>
+#include <iomanip>
+
+#include "midi_byte_array.h"
+
+using namespace std;
+
+namespace MIDI {
+ typedef unsigned char byte;
+ byte sysex = 0xf0;
+ byte eox = 0xf7;
+}
+
+int main()
+{
+ MidiByteArray bytes( 4, 0xf0, 0x01, 0x03, 0x7f );
+ cout << bytes << endl;
+ return 0;
+}
+
diff --git a/libs/surfaces/mackie/types.cc b/libs/surfaces/mackie/types.cc
new file mode 100644
index 0000000000..d2818d7340
--- /dev/null
+++ b/libs/surfaces/mackie/types.cc
@@ -0,0 +1,9 @@
+#include "types.h"
+
+namespace Mackie
+{
+ LedState on( LedState::on );
+ LedState off( LedState::off );
+ LedState flashing( LedState::flashing );
+ LedState none( LedState::none );
+}
diff --git a/libs/surfaces/mackie/types.h b/libs/surfaces/mackie/types.h
new file mode 100644
index 0000000000..2b47e15640
--- /dev/null
+++ b/libs/surfaces/mackie/types.h
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2006,2007 John Anderson
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#ifndef mackie_types_h
+#define mackie_types_h
+
+namespace Mackie
+{
+
+/**
+ This started off as an enum, but it got really annoying
+ typing ? on : off
+*/
+class LedState
+{
+public:
+ enum state_t { none, off, flashing, on };
+ LedState() : _state( none ) {}
+ LedState( bool yn ): _state( yn ? on : off ) {}
+ LedState( state_t state ): _state( state ) {}
+
+ bool operator == ( const LedState & other ) const
+ {
+ return state() == other.state();
+ }
+
+ bool operator != ( const LedState & other ) const
+ {
+ return state() != other.state();
+ }
+
+ state_t state() const { return _state; }
+
+private:
+ state_t _state;
+};
+
+extern LedState on;
+extern LedState off;
+extern LedState flashing;
+extern LedState none;
+
+enum ButtonState { neither = -1, release = 0, press = 1 };
+
+/**
+ Contains the state for a control, with some convenience
+ constructors
+*/
+struct ControlState
+{
+ ControlState(): pos(0.0), delta(0.0), button_state(neither) {}
+
+ ControlState( LedState ls ): pos(0.0), delta(0.0), led_state(ls), button_state(neither) {}
+
+ // Note that this sets both pos and delta to the flt value
+ ControlState( LedState ls, float flt ): pos(flt), delta(flt), ticks(0), led_state(ls), button_state(neither) {}
+ ControlState( float flt ): pos(flt), delta(flt), ticks(0), led_state(none), button_state(neither) {}
+ ControlState( float flt, int tcks ): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {}
+ ControlState( ButtonState bs ): pos(0.0), delta(0.0), ticks(0), led_state(none), button_state(bs) {}
+
+ float pos;
+ float delta;
+ int ticks;
+ LedState led_state;
+ ButtonState button_state;
+};
+
+class Control;
+class Fader;
+class Button;
+class Strip;
+class Group;
+class Pot;
+class Led;
+class LedRing;
+
+}
+
+#endif