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