drastic, fundamental redesign of MCP code
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 10 Apr 2012 14:27:44 +0000 (14:27 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 10 Apr 2012 14:27:44 +0000 (14:27 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@11861 d708f5d6-7413-0410-9779-e7cbd77b26cf

37 files changed:
libs/surfaces/mackie/bcf_surface.cc [deleted file]
libs/surfaces/mackie/bcf_surface.h [deleted file]
libs/surfaces/mackie/button.cc
libs/surfaces/mackie/button.h
libs/surfaces/mackie/controls.cc
libs/surfaces/mackie/controls.h
libs/surfaces/mackie/dummy_port.cc [deleted file]
libs/surfaces/mackie/dummy_port.h [deleted file]
libs/surfaces/mackie/fader.cc
libs/surfaces/mackie/fader.h
libs/surfaces/mackie/jog.h
libs/surfaces/mackie/led.h
libs/surfaces/mackie/ledring.h
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.h
libs/surfaces/mackie/mackie_control_protocol_poll.cc
libs/surfaces/mackie/mackie_jog_wheel.cc
libs/surfaces/mackie/mackie_midi_builder.cc
libs/surfaces/mackie/mackie_midi_builder.h
libs/surfaces/mackie/mackie_port.cc [deleted file]
libs/surfaces/mackie/mackie_port.h [deleted file]
libs/surfaces/mackie/mackie_surface.cc [deleted file]
libs/surfaces/mackie/mackie_surface.h [deleted file]
libs/surfaces/mackie/mcp_buttons.cc
libs/surfaces/mackie/meter.cc
libs/surfaces/mackie/meter.h
libs/surfaces/mackie/pot.h
libs/surfaces/mackie/route_signal.cc [deleted file]
libs/surfaces/mackie/route_signal.h [deleted file]
libs/surfaces/mackie/strip.cc
libs/surfaces/mackie/strip.h
libs/surfaces/mackie/surface.cc
libs/surfaces/mackie/surface.h
libs/surfaces/mackie/surface_port.cc
libs/surfaces/mackie/surface_port.h
libs/surfaces/mackie/types.h
libs/surfaces/mackie/wscript

diff --git a/libs/surfaces/mackie/bcf_surface.cc b/libs/surfaces/mackie/bcf_surface.cc
deleted file mode 100644 (file)
index 756e213..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#include <cmath>
-
-#include "bcf_surface.h"
-#include "controls.h"
-#include "mackie_midi_builder.h"
-#include "surface_port.h"
-#include "jog.h"
-#include "pot.h"
-
-using namespace Mackie;
-
-void 
-BcfSurface::display_bank_start (SurfacePort & port, MackieMidiBuilder & builder, uint32_t current_bank)
-{
-       if  (current_bank == 0) {
-               // send Ar. to 2-char display on the master
-               port.write (builder.two_char_display ("Ar", ".."));
-       } else {
-               // write the current first remote_id to the 2-char display
-               port.write (builder.two_char_display (current_bank));
-       }
-}
-
-void 
-BcfSurface::zero_all (SurfacePort & port, MackieMidiBuilder & builder)
-{
-       // clear 2-char display
-       port.write (builder.two_char_display ("LC"));
-
-       // and the led ring for the master strip
-       blank_jog_ring (port, builder);
-}
-
-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));
-}
-
-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
deleted file mode 100644 (file)
index 47ce8a1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef mackie_surface_bcf_h
-#define mackie_surface_bcf_h
-/*
-       Initially generated by scripts/generate-surface.rb
-*/
-
-#include "surface.h"
-
-namespace Mackie
-{
-
-class MackieButtonHandler;
-
-class BcfSurface : public Surface
-{
-public:
-       BcfSurface (uint32_t max_strips) : Surface (max_strips, 7) {}
-       
-       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 );
-
-};
-
-}
-
-#endif
index 5312f1ae86cb4ab31c1ced67e3fc196058c058de..714294ed7d51ce7be8a7c188664358e0404dcc6a 100644 (file)
@@ -24,9 +24,9 @@
 using namespace Mackie;
 
 Control*
-Button::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
+Button::factory (Surface& surface, int id, const char* name, Group& group)
 {
-       Button* b = new Button (id, ordinal, name, group);
+       Button* b = new Button (id, name, group);
        surface.buttons[id] = b;
        surface.controls.push_back (b);
        group.add (*b);
index 97cd1e309febe74685cb45a9e892e5492ca6a540..2a4dfe849624dbf84b132cf5fab7a3e511a1349a 100644 (file)
@@ -97,15 +97,15 @@ public:
                ButtonUserB = 0x67,
        };
 
-       Button (int id, int ordinal, std::string name, Group & group)
-               : Control (id,  ordinal, name, group)
-               , _led  (id, ordinal, name + "_led", group) {}
+       Button (int id, std::string name, Group & group)
+               : Control (id, name, group)
+               , _led  (id, name + "_led", group) {}
        
        virtual const Led & led() const  { return _led; }
        
        virtual type_t type() const { return type_button; };
 
-       static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
+       static Control* factory (Surface&, int id, const char*, Group&);
        
 private:
        Led _led;
index 7e0a98a007c12db8d71676b3db32017689c1016b..745da219697f439455430dab3ff3b4afcda6c6a6 100644 (file)
@@ -43,9 +43,8 @@ void Group::add (Control& control)
        _controls.push_back (&control);
 }
 
-Control::Control (int id, int ordinal, std::string name, Group & group)
+Control::Control (int id, std::string name, Group & group)
        : _id (id)
-       , _ordinal (ordinal)
        , _name (name)
        , _group (group)
        , _in_use (false)
@@ -82,8 +81,6 @@ ostream & Mackie::operator <<  (ostream & os, const Mackie::Control & control)
        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 << " }";
        
@@ -91,9 +88,9 @@ ostream & Mackie::operator <<  (ostream & os, const Mackie::Control & control)
 }
 
 Control*
-Pot::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
+Pot::factory (Surface& surface, int id, const char* name, Group& group)
 {
-       Pot* p = new Pot (id, ordinal, name, group);
+       Pot* p = new Pot (id, name, group);
        surface.pots[id] = p;
        surface.controls.push_back (p);
        group.add (*p);
@@ -101,9 +98,9 @@ Pot::factory (Surface& surface, int id, int ordinal, const char* name, Group& gr
 }
 
 Control*
-Led::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
+Led::factory (Surface& surface, int id, const char* name, Group& group)
 {
-       Led* l = new Led (id, ordinal, name, group);
+       Led* l = new Led (id, name, group);
        surface.leds[id] = l;
        surface.controls.push_back (l);
        group.add (*l);
@@ -111,9 +108,9 @@ Led::factory (Surface& surface, int id, int ordinal, const char* name, Group& gr
 }
 
 Control*
-Jog::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
+Jog::factory (Surface& surface, int id, const char* name, Group& group)
 {
-       Jog* j = new Jog (id, ordinal, name, group);
+       Jog* j = new Jog (id, name, group);
        surface.controls.push_back (j);
        surface.controls_by_name["jog"] = j;
        group.add (*j);
index 81923f23c37d78dfc74dbaa902575fcc5db1c21c..ca972ed5e07a2ebac6c3ef2c4abe80483e203195 100644 (file)
@@ -62,7 +62,7 @@ public:
                meter_base_id = 0xd0,
        };
        
-       Control (int id, int ordinal, std::string name, Group& group);
+       Control (int id, std::string name, Group& group);
        virtual ~Control() {}
        
        virtual const Led & led() const { throw MackieControlException ("no led available"); }
@@ -76,15 +76,8 @@ public:
        /// unique within the control type.
        int raw_id() const { return _id; }
 
-       /* this identifies a given control within its MCU "bank of 8"
-        */
-       int control_id() const { return _id % 8; }
-       
-       /// The 1-based number of the control
-       int ordinal() const { return _ordinal; }
-       
        const std::string & name() const  { return _name; }
-       const Group & group() const { return _group; }
+       Group & group() const { return _group; }
        virtual bool accepts_feedback() const  { return true; }
        
        virtual type_t type() const = 0;
@@ -105,7 +98,6 @@ public:
 
 private:
        int _id;
-       int _ordinal;
        std::string _name;
        Group& _group;
        bool _in_use;
diff --git a/libs/surfaces/mackie/dummy_port.cc b/libs/surfaces/mackie/dummy_port.cc
deleted file mode 100644 (file)
index 7654f8f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#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
deleted file mode 100644 (file)
index 056e30f..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-       Copyright (C) 2008 John Anderson
-
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-#ifndef dummy_port_h
-#define dummy_port_h
-
-#include "surface_port.h"
-
-#include "midi_byte_array.h"
-
-namespace MIDI {
-       class Port;
-}
-
-namespace Mackie
-{
-
-/**
-       A Dummy Port, to catch things that shouldn't be sent.
-*/
-class DummyPort : public SurfacePort
-{
-public:
-       DummyPort();
-       virtual ~DummyPort();
-       
-       // when this is successful, active() should return true
-       virtual void open();
-       
-       // subclasses should call this before doing their own close
-       virtual void close();
-
-       /// read bytes from the port. They'll either end up in the
-       /// parser, or if that's not active they'll be returned
-       virtual MidiByteArray read();
-       
-       /// an easier way to output bytes via midi
-       virtual void write( const MidiByteArray & );
-
-       virtual const MidiByteArray & sysex_hdr() const;
-       virtual int strips() const;
-
-};     
-
-}
-
-#endif
index a7283f42d13db908930d0453f03f3a37ba9d7cf3..80928d85ebc6c2b1625868e6813e82a6d72e75a5 100644 (file)
 using namespace Mackie;
 
 Control*
-Fader::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
+Fader::factory (Surface& surface, int id, const char* name, Group& group)
 {
-       Fader* f = new Fader (id, ordinal, name, group);
-
-       std::cerr << "Registering fader " << id << " ord " << ordinal << std::endl;
+       Fader* f = new Fader (id, name, group);
 
        surface.faders[id] = f;
        surface.controls.push_back (f);
index b8c72b87535a1fc27b7cf8d272d5e42fcb55ef7b..7c6401682662fc98feaf5dfb5687211cf9f8ffa3 100644 (file)
@@ -7,15 +7,15 @@ namespace Mackie {
 
 class Fader : public Control
 {
-public:
-       Fader (int id, int ordinal, std::string name, Group & group)
-               : Control (id, ordinal, name, group)
+  public:
+       Fader (int id, std::string name, Group & group)
+               : Control (id, name, group)
        {
        }
        
        virtual type_t type() const { return type_fader; }
 
-       static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
+       static Control* factory (Surface&, int id, const char*, Group&);
 };
 
 }
index ec3517348bb3dd1a306ee50a14fc4dd3c157ba93..c769319766aa710b35420d90f3cae5cdc127d27f 100644 (file)
@@ -28,14 +28,14 @@ namespace Mackie {
 class Jog : public Pot
 {
 public:
-       Jog (int id, int ordinal, std::string name, Group & group)
-               : Pot  (id, ordinal, name, group)
+       Jog (int id, std::string name, Group & group)
+               : Pot  (id, name, group)
        {
        }
 
        virtual bool is_jog() const { return true; }
 
-       static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
+       static Control* factory (Surface&, int id, const char*, Group&);
 };
 
 }
index 3577e6f0e4df9270e565c7217ea6444d59fbc9d3..b2bc29ec5f5dfb1f0437c5f4af9df217b257f010 100644 (file)
@@ -27,8 +27,8 @@ namespace Mackie {
 class Led : public Control
 {
 public:
-       Led (int id, int ordinal, std::string name, Group & group)
-               : Control (id, ordinal, name, group)
+       Led (int id, std::string name, Group & group)
+               : Control (id, name, group)
        {
        }
        
@@ -36,7 +36,7 @@ public:
 
        virtual type_t type() const { return type_led; }
 
-       static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
+       static Control* factory (Surface&, int id, const char*, Group&);
 };
 
 }
index 3ab95f211aeee1b187bd99710d558390ee081774..284c2d26a31cca761ad1a945bbba1f3070cf36ac 100644 (file)
@@ -28,8 +28,8 @@ namespace Mackie {
 class LedRing : public Led
 {
 public:
-       LedRing (int id, int ordinal, std::string name, Group & group)
-               : Led (id, ordinal, name, group)
+       LedRing (int id, std::string name, Group & group)
+               : Led (id, name, group)
        {
        }
 
index 96de6b222d0853a9968b180a24e3c848b3af82df..40f62ba6804cc52e11b14ac448f46699b19fde90 100644 (file)
@@ -34,7 +34,6 @@
 
 #include "midi++/types.h"
 #include "midi++/port.h"
-#include "midi++/manager.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/error.h"
 #include "pbd/memento_command.h"
 
 #include "midi_byte_array.h"
 #include "mackie_control_exception.h"
-#include "route_signal.h"
 #include "mackie_midi_builder.h"
 #include "surface_port.h"
 #include "surface.h"
-#include "bcf_surface.h"
-#include "mackie_surface.h"
 
 #include "strip.h"
 #include "control_group.h"
@@ -80,27 +76,26 @@ using namespace PBD;
 
 #include "pbd/abstract_ui.cc" // instantiate template
 
-#define NUCLEUS_DEBUG 1
-
-MackieMidiBuilder builder;
-
 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
 #define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
 
 extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int);
-#define invalidator(x) __invalidator ((x), __FILE__, __LINE__)
+#define invalidator(x) __invalidator (*(MidiControlUI::instance()), __FILE__, __LINE__)
 
 const int MackieControlProtocol::MODIFIER_OPTION = 0x1;
 const int MackieControlProtocol::MODIFIER_CONTROL = 0x2;
 const int MackieControlProtocol::MODIFIER_SHIFT = 0x3;
 const int MackieControlProtocol::MODIFIER_CMDALT = 0x4;
 
+bool MackieControlProtocol::probe()
+{
+       return true;
+}
+
 MackieControlProtocol::MackieControlProtocol (Session& session)
        : ControlProtocol (session, X_("Mackie"), MidiControlUI::instance())
        , AbstractUI<MackieControlUIRequest> ("mackie")
        , _current_initial_bank (0)
-       , _surface (0)
-       , _jog_wheel (*this)
        , _timecode_type (ARDOUR::AnyTime::BBT)
        , _input_bundle (new ARDOUR::Bundle (_("Mackie Control In"), true))
        , _output_bundle (new ARDOUR::Bundle (_("Mackie Control Out"), false))
@@ -143,35 +138,6 @@ MackieControlProtocol::thread_init ()
        ARDOUR::SessionEvent::create_per_thread_pool (X_("MackieControl"), 128);
 }
 
-Mackie::Surface& 
-MackieControlProtocol::surface()
-{
-       if (_surface == 0) {
-               throw MackieControlException ("_surface is 0 in MackieControlProtocol::surface");
-       }
-       return *_surface;
-}
-
-const Mackie::SurfacePort& 
-MackieControlProtocol::mcu_port() const
-{
-       if (_ports.size() < 1) {
-               return _dummy_port;
-       } else {
-               return dynamic_cast<const MackiePort &> (*_ports[0]);
-       }
-}
-
-Mackie::SurfacePort& 
-MackieControlProtocol::mcu_port()
-{
-       if (_ports.size() < 1) {
-               return _dummy_port;
-       } else {
-               return dynamic_cast<MackiePort &> (*_ports[0]);
-       }
-}
-
 // go to the previous track.
 // Assume that get_sorted_routes().size() > route_table.size()
 void 
@@ -189,45 +155,12 @@ void
 MackieControlProtocol::next_track()
 {
        Sorted sorted = get_sorted_routes();
-       if (_current_initial_bank + route_table.size() < sorted.size()) {
+       if (_current_initial_bank + n_strips() < sorted.size()) {
                session->set_dirty();
                switch_banks (_current_initial_bank + 1);
        }
 }
 
-void 
-MackieControlProtocol::clear_route_signals()
-{
-       Glib::Mutex::Lock lm (route_signals_lock);
-
-       for (RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it) {
-               delete *it;
-       }
-
-       route_signals.clear();
-}
-
-// return the port for a given id - 0 based
-// throws an exception if no port found
-MackiePort& 
-MackieControlProtocol::port_for_id (uint32_t index)
-{
-       uint32_t current_max = 0;
-
-       for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) {
-               current_max += (*it)->strips();
-               if (index < current_max) { 
-                       return **it;
-               }
-       }
-
-       // oops - no matching port
-       ostringstream os;
-       os << "No port for index " << index;
-       cerr << "No port for index " << index << endl;
-       throw MackieControlException (os.str());
-}
-
 // predicate for sort call in get_sorted_routes
 struct RouteByRemoteId
 {
@@ -282,19 +215,41 @@ MackieControlProtocol::get_sorted_routes()
 void 
 MackieControlProtocol::refresh_current_bank()
 {
-       switch_banks (_current_initial_bank);
+       switch_banks (_current_initial_bank, true);
+}
+
+uint32_t
+MackieControlProtocol::n_strips() const
+{
+       uint32_t strip_count = 0;
+
+       for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) {
+               if ((*si)->active()) {
+                       strip_count += (*si)->n_strips ();
+               }
+       }
+
+       return strip_count;
 }
 
 void 
-MackieControlProtocol::switch_banks (int initial)
+MackieControlProtocol::switch_banks (uint32_t initial, bool force)
 {
-       // DON'T prevent bank switch if initial == _current_initial_bank
-       // because then this method can't be used as a refresh
+       if (initial == _current_initial_bank && !force) {
+               return;
+       }
 
        Sorted sorted = get_sorted_routes();
-       int delta = sorted.size() - route_table.size();
+       uint32_t strip_cnt = n_strips();
+
+       if (sorted.size() <= strip_cnt) {
+               /* no banking */
+               return;
+       }
+
+       uint32_t delta = sorted.size() - strip_cnt;
 
-       if (initial < 0 || (delta > 0 && initial > delta)) {
+       if (delta > 0 && initial > delta) {
                DEBUG_TRACE (DEBUG::MackieControl, string_compose ("not switching to %1\n", initial));
                return;
        }
@@ -302,79 +257,47 @@ MackieControlProtocol::switch_banks (int initial)
        _current_initial_bank = initial;
        _current_selected_track = -1;
 
-       clear_route_signals();
-
-       // now set the signals for new routes
-       if (_current_initial_bank <= sorted.size()) {
+       for (Surfaces::iterator si = surfaces.begin(); si != surfaces.end(); ++si) {
+               (*si)->drop_routes ();
+       }
 
-               uint32_t end_pos = min (route_table.size(), sorted.size());
-               uint32_t i = 0;
-               Sorted::iterator it = sorted.begin() + _current_initial_bank;
-               Sorted::iterator end = sorted.begin() + _current_initial_bank + end_pos;
+       // Map current bank of routes onto each surface(+strip)
 
-               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2\n", _current_initial_bank, end_pos));
+       if (_current_initial_bank <= sorted.size()) {
 
-               route_table.clear ();
-               set_route_table_size (surface().strips.size());
+               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to %1, %2\n", _current_initial_bank, strip_cnt));
 
                // link routes to strips
 
-               Glib::Mutex::Lock lm (route_signals_lock);
-
-               for (; it != end && it != sorted.end(); ++it, ++i) {
-                       boost::shared_ptr<Route> route = *it;
-
-                       assert (surface().strips[i]);
-                       Strip & strip = *surface().strips[i];
-
-                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("remote id %1 connecting %2 to %3 with port %4\n", 
-                                                                          route->remote_control_id(), route->name(), strip.name(), port_for_id(i)));
-                       set_route_table (i, route);
-                       RouteSignal * rs = new RouteSignal (route, *this, strip, port_for_id(i));
-                       route_signals.push_back (rs);
-                       rs->notify_all ();
-               }
+               Surfaces::iterator si = surfaces.begin();
+               uint32_t surface_strip_cnt = (*si)->n_strips();
+               uint32_t surface_strip = 0;
+               
+               for (Sorted::iterator r = sorted.begin() + _current_initial_bank; r != sorted.end(); ++r) {
 
-               // create dead strips if there aren't enough routes to
-               // fill a bank
-               for (; i < route_table.size(); ++i) {
-                       Strip & strip = *surface().strips[i];
-                       // send zero for this strip
-                       MackiePort & port = port_for_id(i);
-                       port.write (builder.zero_strip (port, strip));
-               }
-       }
+                       Strip* strip = (*si)->nth_strip (surface_strip);
 
-       // display the current start bank.
-       surface().display_bank_start (mcu_port(), builder, _current_initial_bank);
-}
+                       if (strip) {
+                               strip->set_route (*r);
+                       }
 
-void 
-MackieControlProtocol::zero_all()
-{
-       // TODO turn off Timecode displays
+                       if (surface_strip == surface_strip_cnt) {
 
-       // zero all strips
-       for (Surface::Strips::iterator it = surface().strips.begin(); it != surface().strips.end(); ++it) {
-               MackiePort & port = port_for_id ((*it)->index());
-               port.write (builder.zero_strip (port, **it));
-       }
+                               /* move to next surface */
 
-       // and the master strip
-       mcu_port().write (builder.zero_strip (dynamic_cast<MackiePort&> (mcu_port()), master_strip()));
+                               surface_strip = 0;
+                               ++si;
 
-       // turn off global buttons and leds
-       // global buttons are only ever on mcu_port, so we don't have
-       // to figure out which port.
-       for (Surface::Controls::iterator it = surface().controls.begin(); it != surface().controls.end(); ++it) {
-               Control & control = **it;
-               if (!control.group().is_strip() && control.accepts_feedback()) {
-                       mcu_port().write (builder.zero_control (control));
+                               if (si == surfaces.end()) {
+                                       break;
+                               }
+                               surface_strip_cnt += (*si)->n_strips();
+                       }
                }
        }
 
-       // any hardware-specific stuff
-       surface().zero_all (mcu_port(), builder);
+       // display the current start bank.
+       surfaces.front()->display_bank_start (_current_initial_bank);
 }
 
 int 
@@ -386,51 +309,20 @@ MackieControlProtocol::set_active (bool yn)
 
        try
        {
-               // the reason for the locking and unlocking is that
-               // glibmm can't do a condition wait on a RecMutex
                if (yn) {
-                       // TODO what happens if this fails half way?
 
-                       // start an event loop
-
-                       BaseUI::run ();
-                       
-                       // create MackiePorts
-                       {
-                               Glib::Mutex::Lock lock (update_mutex);
-                               create_ports();
-                       }
-                       
-                       // now initialise MackiePorts - ie exchange sysex messages
-                       for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) {
-                               (*it)->open();
-                       }
-                       
-                       // wait until all ports are active
-                       // TODO a more sophisticated approach would
-                       // allow things to start up with only an MCU, even if
-                       // extenders were specified but not responding.
-                       for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) {
-                               (*it)->wait_for_init();
-                       }
+                       create_surfaces ();
+                       connect_session_signals ();
                        
-                       // create surface object. This depends on the ports being
-                       // correctly initialised
-                       initialize_surface();
-                       connect_session_signals();
-                       
-                       // yeehah!
                        _active = true;
-                       
-                       // send current control positions to surface
-                       // must come after _active = true otherwise it won't run
-                       update_surface();
+                       update_surfaces ();
 
-                       Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds
+                       /* set up periodic task for metering and automation
+                        */
 
+                       Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds
                        periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::periodic));
-
-                       periodic_timeout->attach (main_loop()->get_context());
+                       periodic_timeout->attach (MidiControlUI::instance()->main_loop()->get_context());
 
                } else {
                        BaseUI::quit ();
@@ -455,84 +347,15 @@ MackieControlProtocol::periodic ()
                return false;
        }
 
-       {
-               Glib::Mutex::Lock lm (route_signals_lock);
-               for (RouteSignals::iterator rs = route_signals.begin(); rs != route_signals.end(); ++rs) {
-                       update_automation (**rs);
-                       float dB = const_cast<PeakMeter&> ((*rs)->route()->peak_meter()).peak_power (0);
-                       (*rs)->port().write ((*rs)->strip().meter().update_message (dB));
-               }
-
-               // and the master strip
-               if (master_route_signal != 0) {
-                       update_automation (*master_route_signal);
-               }
+       for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+               (*s)->periodic ();
        }
-
+       
        update_timecode_display();
 
        return true;
 }
 
-bool 
-MackieControlProtocol::handle_strip_button (SurfacePort & port, Control & control, ButtonState bs, boost::shared_ptr<Route> route)
-{
-       bool state = false;
-
-       if (bs == press) {
-               if (control.name() == "recenable") {
-                       state = !route->record_enabled();
-                       route->set_record_enabled (state, this);
-               } else if (control.name() == "mute") {
-                       state = !route->muted();
-                       route->set_mute (state, this);
-               } else if (control.name() == "solo") {
-                       state = !route->soloed();
-                       route->set_solo (state, this);
-               } else if (control.name() == "select") {
-                       Strip* strip = const_cast<Strip*>(dynamic_cast<const Strip*>(&control.group()));
-                       if (strip) {
-
-                               if ((uint32_t) strip->index() < route_table.size()) {
-                                       boost::shared_ptr<Route> r = route_table[strip->index()];
-
-                                       if (_modifier_state == MODIFIER_SHIFT) {
-                                               r->gain_control()->set_value (0.0);
-                                       } else {
-                                               if (r->remote_control_id() == _current_selected_track) {
-                                                       UnselectTrack (); /* EMIT SIGNAL */
-                                                       _current_selected_track = -1;
-                                               } else {
-                                                       SelectByRID (r->remote_control_id()); /* EMIT SIGNAL */
-                                                       _current_selected_track = r->remote_control_id();;
-                                               }
-                                       }
-                               } 
-                       }
-               } else if (control.name() == "vselect") {
-                       // TODO could be used to select different things to apply the pot to?
-                       //state = default_button_press (dynamic_cast<Button&> (control));
-               }
-       }
-
-       if (control.name() == "fader_touch") {
-               state = bs == press;
-               Strip* strip = const_cast<Strip*>(dynamic_cast<const Strip*>(&control.group()));
-               
-               if (strip) {
-                       strip->gain().set_in_use (state);
-
-                       if (ARDOUR::Config->get_mackie_emulation() == "bcf" && state) {
-                               /* BCF faders don't support touch, so add a timeout to reset
-                                  their `in_use' state.
-                               */
-                               add_in_use_timeout (port, strip->gain(), &strip->fader_touch());
-                       }
-               }
-       }
-
-       return state;
-}
 
 void 
 MackieControlProtocol::update_timecode_beats_led()
@@ -556,9 +379,15 @@ MackieControlProtocol::update_timecode_beats_led()
 void 
 MackieControlProtocol::update_global_button (const string & name, LedState ls)
 {
-       if (surface().controls_by_name.find (name) != surface().controls_by_name.end()) {
-               Button * button = dynamic_cast<Button*> (surface().controls_by_name[name]);
-               mcu_port().write (builder.build_led (button->led(), ls));
+       boost::shared_ptr<Surface> surface = surfaces.front();
+
+       if (!surface->type() == mcu) {
+               return;
+       }
+
+       if (surface->controls_by_name.find (name) != surface->controls_by_name.end()) {
+               Button * button = dynamic_cast<Button*> (surface->controls_by_name[name]);
+               surface->write (builder.build_led (button->led(), ls));
        } else {
                DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Button %1 not found\n", name));
        }
@@ -567,9 +396,15 @@ MackieControlProtocol::update_global_button (const string & name, LedState ls)
 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));
+       boost::shared_ptr<Surface> surface = surfaces.front();
+
+       if (!surface->type() == mcu) {
+               return;
+       }
+
+       if (surface->controls_by_name.find (name) != surface->controls_by_name.end()) {
+               Led * led = dynamic_cast<Led*> (surface->controls_by_name[name]);
+               surface->write (builder.build_led (*led, ls));
        } else {
                DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Led %1 not found\n", name));
        }
@@ -577,7 +412,7 @@ MackieControlProtocol::update_global_led (const string & name, LedState ls)
 
 // send messages to surface to set controls to correct values
 void 
-MackieControlProtocol::update_surface()
+MackieControlProtocol::update_surfaces()
 {
        if (!_active) {
                return;
@@ -585,22 +420,13 @@ MackieControlProtocol::update_surface()
 
        // do the initial bank switch to connect signals
        // _current_initial_bank is initialised by set_state
-       switch_banks (_current_initial_bank);
-       
-       /* Create a RouteSignal for the master route, if we don't already have one */
-       if (!master_route_signal) {
-               boost::shared_ptr<Route> mr = master_route ();
-               if (mr) {
-                       master_route_signal = boost::shared_ptr<RouteSignal> (new RouteSignal (mr, *this, master_strip(), mcu_port()));
-                       // update strip from route
-                       master_route_signal->notify_all();
-               }
-       }
+       switch_banks (_current_initial_bank, true);
        
        // sometimes the jog wheel is a pot
-       surface().blank_jog_ring (mcu_port(), builder);
+       surfaces.front()->blank_jog_ring ();
        
        // update global buttons and displays
+
        notify_record_state_changed();
        notify_transport_state_changed();
        update_timecode_beats_led();
@@ -631,157 +457,45 @@ MackieControlProtocol::connect_session_signals()
 }
 
 void 
-MackieControlProtocol::add_port (MIDI::Port & midi_input_port, MIDI::Port & midi_output_port, int number, MackiePort::port_type_t port_type)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1 %2\n", midi_input_port.name(), midi_output_port.name()));
-
-       MackiePort * sport = new MackiePort (*this, midi_input_port, midi_output_port, number, port_type);
-       _ports.push_back (sport);
-       
-       sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
-       sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
-       sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
-
-       _input_bundle->add_channel (
-               midi_input_port.name(),
-               ARDOUR::DataType::MIDI,
-               session->engine().make_port_name_non_relative (midi_input_port.name())
-               );
-       
-       _output_bundle->add_channel (
-               midi_output_port.name(),
-               ARDOUR::DataType::MIDI,
-               session->engine().make_port_name_non_relative (midi_output_port.name())
-               );
-}
-
-void 
-MackieControlProtocol::create_ports()
+MackieControlProtocol::create_surfaces ()
 {
-       MIDI::Manager * mm = MIDI::Manager::instance();
-       MIDI::Port * midi_input_port = mm->add_port (
-               new MIDI::Port (string_compose (_("%1 in"), default_port_name), MIDI::Port::IsInput, session->engine().jack())
-               );
-       MIDI::Port * midi_output_port = mm->add_port (
-               new MIDI::Port (string_compose (_("%1 out"), default_port_name), MIDI::Port::IsOutput, session->engine().jack())
-               );
+       string device_name = "mcu";
+       surface_type_t stype = mcu;
 
-       /* Create main port */
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n",
+                                                          1 + ARDOUR::Config->get_mackie_extenders()));
 
-       if (!midi_input_port->ok() || !midi_output_port->ok()) {
-               ostringstream os;
-               os << _("Mackie control MIDI ports could not be created; Mackie control disabled");
-               error << os.str() << endmsg;
-               throw MackieControlException (os.str());
-       }
+       for (uint32_t n = 0; n < 1 + ARDOUR::Config->get_mackie_extenders(); ++n) {
 
-       add_port (*midi_input_port, *midi_output_port, 0, MackiePort::mcu);
-
-       /* Create extender ports */
+               boost::shared_ptr<Surface> surface (new Surface (*this, session->engine().jack(), device_name, n, stype));
+               surfaces.push_back (surface);
+               
+               device_name = "mcu_xt";
+               stype = ext;
 
-       for (uint32_t index = 1; index <= Config->get_mackie_extenders(); ++index) {
-               MIDI::Port * midi_input_port = mm->add_port (
-                       new MIDI::Port (string_compose (_("mcu_xt_%1 in"), index), MIDI::Port::IsInput, session->engine().jack())
+               _input_bundle->add_channel (
+                       surface->port().input_port().name(),
+                       ARDOUR::DataType::MIDI,
+                       session->engine().make_port_name_non_relative (surface->port().input_port().name())
                        );
-               MIDI::Port * midi_output_port = mm->add_port (
-                       new MIDI::Port (string_compose (_("mcu_xt_%1 out"), index), MIDI::Port::IsOutput, session->engine().jack())
+               
+               _output_bundle->add_channel (
+                       surface->port().output_port().name(),
+                       ARDOUR::DataType::MIDI,
+                       session->engine().make_port_name_non_relative (surface->port().output_port().name())
                        );
-               if (midi_input_port->ok() && midi_output_port->ok()) {
-                       add_port (*midi_input_port, *midi_output_port, index, MackiePort::ext);
-               }
-       }
-}
-
-boost::shared_ptr<Route> 
-MackieControlProtocol::master_route()
-{
-       return session->master_out ();
-}
-
-Strip& 
-MackieControlProtocol::master_strip()
-{
-       return dynamic_cast<Strip&> (*surface().groups["master"]);
-}
-
-void 
-MackieControlProtocol::initialize_surface()
-{
-       // set up the route table
-       int strips = 0;
-       for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) {
-               strips += (*it)->strips();
-       }
-
-       set_route_table_size (strips);
-
-       // TODO same as code in mackie_port.cc
-       string emulation = ARDOUR::Config->get_mackie_emulation();
-       if (emulation == "bcf") {
-               _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();
 }
 
 void 
 MackieControlProtocol::close()
 {
-
-       // must be before other shutdown otherwise polling loop
-       // calls methods on objects that are deleted
-
        port_connections.drop_connections ();
        session_connections.drop_connections ();
        route_connections.drop_connections ();
        periodic_connection.disconnect ();
 
-       if (_surface != 0) {
-               // These will fail if the port has gone away.
-               // So catch the exception and do the rest of the
-               // close afterwards
-               // because the bcf doesn't respond to the next 3 sysex messages
-               try {
-                       zero_all();
-               }
-
-               catch (exception & e) {
-                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::close caught exception: %1\n", e.what()));
-               }
-
-               for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) {
-                       try {
-                               MackiePort & port = **it;
-                               // faders to minimum
-                               port.write_sysex (0x61);
-                               // All LEDs off
-                               port.write_sysex (0x62);
-                               // Reset (reboot into offline mode)
-                               port.write_sysex (0x63);
-                       }
-                       catch (exception & e) {
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieControlProtocol::close caught exception: %1\n", e.what()));
-                       }
-               }
-
-               // disconnect routes from strips
-               clear_route_signals();
-               delete _surface;
-               _surface = 0;
-       }
-
-       // shut down MackiePorts
-       for (MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it) {
-               delete *it;
-       }
-
-       _ports.clear();
+       surfaces.clear ();
 }
 
 XMLNode& 
@@ -828,120 +542,6 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/)
        return retval;
 }
 
-void 
-MackieControlProtocol::handle_control_event (SurfacePort & port, Control & control, const ControlState & state)
-{
-       // find the route for the control, if there is one
-       boost::shared_ptr<Route> route;
-
-       if (control.group().is_strip()) {
-               if (control.group().is_master()) {
-                       DEBUG_TRACE (DEBUG::MackieControl, "master strip control event\n");
-                       route = master_route();
-               } else {
-                       uint32_t index = control.ordinal() - 1 + (port.number() * port.strips());
-                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip control event, index = %1, rt size = %2\n",
-                                                                          index, route_table.size()));
-                       if (index < route_table.size()) {
-                               route = route_table[index];
-                               if (route) {
-                                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("modifying %1\n", route->name()));
-                               } else {
-                                       DEBUG_TRACE (DEBUG::MackieControl, "no route found!\n");
-                               }
-                       } else {
-                               cerr << "Warning: index is " << index << " which is not in the route table, size: " << route_table.size() << endl;
-                               DEBUG_TRACE (DEBUG::MackieControl, "illegal route index found!\n");
-                       }
-               }
-       }
-
-       // This handles control element events from the surface
-       // the state of the controls on the surface is usually updated
-       // from UI events.
-       switch (control.type()) {
-               case Control::type_fader:
-                       // 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)
-                       {
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", state.pos));
-
-                               route->gain_control()->set_value (slider_position_to_gain (state.pos));
-
-                               if (ARDOUR::Config->get_mackie_emulation() == "bcf") {
-                                       /* reset the timeout while we're still moving the fader */
-                                       add_in_use_timeout (port, control, control.in_use_touch_control);
-                               }
-
-                               // 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;
-
-               case Control::type_button:
-                       if (control.group().is_strip()) {
-                               // strips
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip button %1\n", control.id()));
-                               if (route != 0) {
-                                       handle_strip_button (port, control, state.button_state, route);
-                               } else {
-                                       // no route so always switch the light off
-                                       // because no signals will be emitted by a non-route
-                                       port.write (builder.build_led (control.led(), off));
-                               }
-                       } else if (control.group().is_master()) {
-                               // master fader touch
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("master strip button %1\n", control.id()));
-                               if (route != 0) {
-                                       handle_strip_button (port, control, state.button_state, route);
-                               }
-                       } else {
-                               // handle all non-strip buttons
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", control.id()));
-                               handle_button_event (dynamic_cast<Button&>(control), state.button_state);
-
-                       }
-                       break;
-
-               // pot (jog wheel, external control)
-               case Control::type_pot:
-                       if (control.group().is_strip()) {
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip pot %1\n", control.id()));
-                               if (route) {
-                                        boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
-                                       // pan for mono input routes, or stereo linked panners
-                                        if (panner) {
-                                               double p = panner->position ();
-                                                
-                                               // calculate new value, and adjust
-                                               p += state.delta * state.sign;
-                                               p = min (1.0, p);
-                                               p = max (0.0, p);
-                                               panner->set_position (p);
-                                       }
-                               } else {
-                                       // it's a pot for an umnapped route, so turn all the lights off
-                                       port.write (builder.build_led_ring (dynamic_cast<Pot &> (control), off));
-                               }
-                       } else {
-                               if (control.is_jog()) {
-                                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", state.ticks));
-                                       _jog_wheel.jog_event (port, control, state);
-                               } else {
-                                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("External controller moved %1\n", state.ticks));
-                                       cout << "external controller" << state.ticks * state.sign << endl;
-                               }
-                       }
-                       break;
-
-               default:
-                       cout << "Control::type not handled: " << control.type() << endl;
-       }
-}
 
 /////////////////////////////////////////////////
 // handlers for Route signals
@@ -950,156 +550,7 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
 // from Route, but they're also used in polling for automation
 /////////////////////////////////////////////////
 
-void 
-MackieControlProtocol::notify_solo_changed (RouteSignal * route_signal)
-{
-       try {
-               Button & button = route_signal->strip().solo();
-               route_signal->port().write (builder.build_led (button, route_signal->route()->soloed()));
-       }
-       catch (exception & e) {
-               cout << e.what() << endl;
-       }
-}
-
-void 
-MackieControlProtocol::notify_mute_changed (RouteSignal * route_signal)
-{
-       try {
-               Button & button = route_signal->strip().mute();
-               route_signal->port().write (builder.build_led (button, route_signal->route()->muted()));
-       }
-       catch (exception & e) {
-               cout << e.what() << endl;
-       }
-}
-
-void 
-MackieControlProtocol::notify_record_enable_changed (RouteSignal * route_signal)
-{
-       try {
-               Button & button = route_signal->strip().recenable();
-               route_signal->port().write (builder.build_led (button, route_signal->route()->record_enabled()));
-       }
-       catch (exception & e) {
-               cout << e.what() << endl;
-       }
-}
-
-void MackieControlProtocol::notify_active_changed (RouteSignal *)
-{
-       try {
-               DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::notify_active_changed\n");
-               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();
-               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("route %1 gain change, update fader %2 on port %3\n", 
-                                                                  route_signal->route()->name(), 
-                                                                  fader.raw_id(),
-                                                                  route_signal->port().output_port().name()));
-               if (!fader.in_use()) {
-                       float gain_value = gain_to_slider_position (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) {
-               cout << e.what() << endl;
-       }
-}
-
-void 
-MackieControlProtocol::notify_property_changed (const PropertyChange& what_changed, RouteSignal * route_signal)
-{
-       if (!what_changed.contains (Properties::name)) {
-               return;
-       }
-
-       try {
-               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);
-                       }
-
-#ifdef NUCLEUS_DEBUG
-                       cerr << "show strip name from " << fullname << " as " << line1 << endl;
-#endif
-
-                       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) {
-               cout << e.what() << endl;
-       }
-}
-
-void 
-MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool force_update)
-{
-       try {
-               Pot & pot = route_signal->strip().vpot();
-               boost::shared_ptr<Panner> panner = route_signal->route()->panner();
-               if (panner) {
-                       double pos = panner->position ();
-
-                       // 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 {
-                       route_signal->port().write (builder.zero_control (pot));
-               }
-       }
-       catch (exception & e) {
-               cout << e.what() << endl;
-       }
-}
-
 // TODO handle plugin automation polling
-void 
-MackieControlProtocol::update_automation (RouteSignal & rs)
-{
-       ARDOUR::AutoState gain_state = rs.route()->gain_control()->automation_state();
-
-       if (gain_state == Touch || gain_state == Play) {
-               notify_gain_changed (&rs, false);
-       }
-
-       if (rs.route()->panner()) {
-               ARDOUR::AutoState panner_state = rs.route()->panner()->automation_state();
-               if (panner_state == Touch || panner_state == Play) {
-                       notify_panner_changed (&rs, false);
-               }
-       }
-}
-
 string 
 MackieControlProtocol::format_bbt_timecode (framepos_t now_frame)
 {
@@ -1150,33 +601,34 @@ MackieControlProtocol::format_timecode_timecode (framepos_t now_frame)
 void 
 MackieControlProtocol::update_timecode_display()
 {
-       if (surface().has_timecode_display()) {
-               // do assignment here so current_frame is fixed
-               framepos_t current_frame = session->transport_frame();
-               string timecode;
-
-               switch (_timecode_type) {
-                       case ARDOUR::AnyTime::BBT:
-                               timecode = format_bbt_timecode (current_frame);
-                               break;
-                       case ARDOUR::AnyTime::Timecode:
-                               timecode = format_timecode_timecode (current_frame);
-                               break;
-                       default:
-                               ostringstream os;
-                               os << "Unknown timecode: " << _timecode_type;
-                               throw runtime_error (os.str());
-               }
-
-               // only write the timecode string to the MCU if it's changed
-               // since last time. This is to reduce midi bandwidth used.
-               if (timecode != _timecode_last) {
-                       surface().display_timecode (mcu_port(), builder, timecode, _timecode_last);
-                       _timecode_last = timecode;
-               }
+       boost::shared_ptr<Surface> surface = surfaces.front();
+
+       if (surface->type() != mcu || !surface->has_timecode_display()) {
+               return;
        }
-}
 
+       // do assignment here so current_frame is fixed
+       framepos_t current_frame = session->transport_frame();
+       string timecode;
+
+       switch (_timecode_type) {
+       case ARDOUR::AnyTime::BBT:
+               timecode = format_bbt_timecode (current_frame);
+               break;
+       case ARDOUR::AnyTime::Timecode:
+               timecode = format_timecode_timecode (current_frame);
+               break;
+       default:
+               return;
+       }
+       
+       // only write the timecode string to the MCU if it's changed
+       // since last time. This is to reduce midi bandwidth used.
+       if (timecode != _timecode_last) {
+               surface->display_timecode (timecode, _timecode_last);
+               _timecode_last = timecode;
+       }
+}
 
 ///////////////////////////////////////////
 // Session signals
@@ -1202,12 +654,7 @@ MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl)
        // currently assigned banks are less than the full set of
        // strips, so activate the new strip now.
 
-       {
-               Glib::Mutex::Lock lm (route_signals_lock);
-               if (route_signals.size() < route_table.size()) {
-                       refresh_current_bank();
-               }
-       }
+       refresh_current_bank();
 
        // otherwise route added, but current bank needs no updating
 
@@ -1222,25 +669,24 @@ MackieControlProtocol::notify_route_added (ARDOUR::RouteList & rl)
 void 
 MackieControlProtocol::notify_solo_active_changed (bool active)
 {
-       Button * rude_solo = reinterpret_cast<Button*> (surface().controls_by_name["solo"]);
-       mcu_port().write (builder.build_led (*rude_solo, active ? flashing : off));
+       boost::shared_ptr<Surface> surface = surfaces.front();
+       
+       Button * rude_solo = reinterpret_cast<Button*> (surface->controls_by_name["solo"]);
+
+       if (rude_solo) {
+               surface->write (builder.build_led (*rude_solo, active ? flashing : off));
+       }
 }
 
 void 
 MackieControlProtocol::notify_remote_id_changed()
 {
        Sorted sorted = get_sorted_routes();
+       uint32_t sz = n_strips();
 
        // if a remote id has been moved off the end, we need to shift
        // the current bank backwards.
 
-       uint32_t sz;
-
-       {
-               Glib::Mutex::Lock lm (route_signals_lock);
-               sz = route_signals.size();
-       }
-
        if (sorted.size() - _current_initial_bank < sz) {
                // but don't shift backwards past the zeroth channel
                switch_banks (max((Sorted::size_type) 0, sorted.size() - sz));
@@ -1258,8 +704,10 @@ void
 MackieControlProtocol::notify_record_state_changed()
 {
        // switch rec button on / off / flashing
-       Button * rec = reinterpret_cast<Button*> (surface().controls_by_name["record"]);
-       mcu_port().write (builder.build_led (*rec, record_release (*rec)));
+       Button * rec = reinterpret_cast<Button*> (surfaces.front()->controls_by_name["record"]);
+       if (rec) {
+               surfaces.front()->write (builder.build_led (*rec, record_release (*rec)));
+       }
 }
 
 void 
@@ -1273,37 +721,12 @@ MackieControlProtocol::notify_transport_state_changed()
        _transport_previously_rolling = session->transport_rolling();
 
        // rec is special because it's tristate
-       Button * rec = reinterpret_cast<Button*> (surface().controls_by_name["record"]);
-       mcu_port().write (builder.build_led (*rec, record_release (*rec)));
-}
-
-
-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;
+       Button * rec = reinterpret_cast<Button*> (surfaces.front()->controls_by_name["record"]);
+       if (rec) {
+               surfaces.front()->write (builder.build_led (*rec, record_release (*rec)));
        }
 }
 
-
 list<boost::shared_ptr<ARDOUR::Bundle> >
 MackieControlProtocol::bundles ()
 {
@@ -1324,20 +747,12 @@ MackieControlProtocol::port_connected_or_disconnected (string a, string b, bool
                return;
        }
 
-       MackiePorts::const_iterator i = _ports.begin();
-       while (i != _ports.end()) {
-
-               string const n = AudioEngine::instance()->make_port_name_non_relative ((*i)->output_port().name ());
-
+       for (Surfaces::iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
+               string const n = AudioEngine::instance()->make_port_name_non_relative ((*s)->port().output_port().name ());
                if (a == n || b == n) {
-                       break;
+                       update_surfaces ();
+                       return;
                }
-
-               ++i;
-       }
-
-       if (i != _ports.end ()) {
-               update_surface ();
        }
 }
 
@@ -1367,19 +782,19 @@ MackieControlProtocol::stop ()
  *  @param touch_control a touch control to emit an event for, or 0.
  */
 void
-MackieControlProtocol::add_in_use_timeout (SurfacePort& port, Control& in_use_control, Control* touch_control)
+MackieControlProtocol::add_in_use_timeout (Surface& surface, Control& in_use_control, Control* touch_control)
 {
        Glib::RefPtr<Glib::TimeoutSource> timeout (Glib::TimeoutSource::create (250)); // milliseconds
 
        in_use_control.in_use_connection.disconnect ();
        in_use_control.in_use_connection = timeout->connect (
-               sigc::bind (sigc::mem_fun (*this, &MackieControlProtocol::control_in_use_timeout), &port, &in_use_control, touch_control));
+               sigc::bind (sigc::mem_fun (*this, &MackieControlProtocol::control_in_use_timeout), &surface, &in_use_control, touch_control));
        in_use_control.in_use_touch_control = touch_control;
+       
+       timeout->attach (MidiControlUI::instance()->main_loop()->get_context());
 
-       timeout->attach (main_loop()->get_context());
-
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("timeout queued for port %1, control %2 touch control %3\n",
-                                                          &port, &in_use_control, touch_control));}
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("timeout queued for surface %1, control %2 touch control %3\n",
+                                                          surface.number(), &in_use_control, touch_control));}
 
 /** Handle timeouts to reset in_use for controls that can't
  *  do this by themselves (e.g. pots, and faders without touch support).
@@ -1387,17 +802,17 @@ MackieControlProtocol::add_in_use_timeout (SurfacePort& port, Control& in_use_co
  *  @param touch_control a touch control to emit an event for, or 0.
  */
 bool
-MackieControlProtocol::control_in_use_timeout (SurfacePort* port, Control* in_use_control, Control* touch_control)
+MackieControlProtocol::control_in_use_timeout (Surface* surface, Control* in_use_control, Control* touch_control)
 {
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("timeout elapsed for port %1, control %2 touch control %3\n",
-                                                          port, in_use_control, touch_control));
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("timeout elapsed for surface %1, control %2 touch control %3\n",
+                                                          surface->number(), in_use_control, touch_control));
 
        in_use_control->set_in_use (false);
 
        if (touch_control) {
                // empty control_state
                ControlState control_state;
-               handle_control_event (*port, *touch_control, control_state);
+               surface->handle_control_event (*touch_control, control_state);
        }
        
        // only call this method once from the timer
@@ -1405,29 +820,18 @@ MackieControlProtocol::control_in_use_timeout (SurfacePort* port, Control* in_us
 }
 
 void 
-MackieControlProtocol::update_led (Button& button, Mackie::LedState ls)
+MackieControlProtocol::update_led (Surface& surface, Button& button, Mackie::LedState ls)
 {
        if (ls != none) {
-               SurfacePort * port = 0;
-
-               if (button.group().is_strip()) {
-                       if (button.group().is_master()) {
-                               port = &mcu_port();
-                       } else {
-                               port = &port_for_id (dynamic_cast<const Strip&> (button.group()).index());
-                       }
-               } else {
-                       port = &mcu_port();
-               }
-               port->write (builder.build_led (button, ls));
+               surface.port().write (builder.build_led (button, ls));
        }
 }
 
 void 
-MackieControlProtocol::handle_button_event (Button& button, ButtonState bs)
+MackieControlProtocol::handle_button_event (Surface& surface, Button& button, ButtonState bs)
 {
        if  (bs != press && bs != release) {
-               update_led (button, none);
+               update_led (surface, button, none);
                return;
        }
        
@@ -1950,5 +1354,22 @@ MackieControlProtocol::handle_button_event (Button& button, ButtonState bs)
 
        }
 
-       update_led (button, ls);
+       update_led (surface, button, ls);
+}
+
+void
+MackieControlProtocol::select_track (boost::shared_ptr<Route> r)
+{
+       if (_modifier_state == MODIFIER_SHIFT) {
+               r->gain_control()->set_value (0.0);
+       } else {
+               if (_current_selected_track > 0 && r->remote_control_id() == (uint32_t) _current_selected_track) {
+                       UnselectTrack (); /* EMIT SIGNAL */
+                       _current_selected_track = -1;
+               } else {
+                       SelectByRID (r->remote_control_id()); /* EMIT SIGNAL */
+                       _current_selected_track = r->remote_control_id();;
+               }
+       }
 }
+
index b7f3a2b30d60527bcee366da5fd2a75529ed1476..0232cd46d7159a3f8acb58c2d8815eb5bd6ab5f5 100644 (file)
 
 #include <sys/time.h>
 #include <pthread.h>
+#include <boost/smart_ptr.hpp>
 
 #include <glibmm/thread.h>
 
 #include "pbd/abstract_ui.h"
 
-#include "ardour/types.h"
-#include "ardour/midi_ui.h"
 #include "midi++/types.h"
 
+#include "ardour/types.h"
+
 #include "control_protocol/control_protocol.h"
 #include "midi_byte_array.h"
 #include "controls.h"
-#include "dummy_port.h"
-#include "route_signal.h"
 #include "mackie_port.h"
 #include "mackie_jog_wheel.h"
+#include "mackie_midi_builder.h"
 #include "timer.h"
 
 namespace MIDI {
@@ -92,36 +92,28 @@ class MackieControlProtocol
   
        static bool probe();
        
-       Mackie::Surface & surface();
+       typedef std::list<boost::shared_ptr<Mackie::Surface> > Surfaces;
+       Surfaces surfaces;
 
        std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
 
+       uint32_t n_strips () const;
+       
        bool has_editor () const { return true; }
        void* get_gui () const;
        void tear_down_gui ();
+
+       void select_track (boost::shared_ptr<ARDOUR::Route> r);
        
-       // control events
-       void handle_control_event (Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state);
-       void handle_button_event (Mackie::Button& button, Mackie::ButtonState);
-
-       // strip/route related stuff
-  public:      
-       void notify_solo_changed (Mackie::RouteSignal *);
-       void notify_mute_changed (Mackie::RouteSignal *);
-       void notify_record_enable_changed (Mackie::RouteSignal *);
-       void notify_gain_changed (Mackie::RouteSignal *, bool force_update = true);
-       void notify_property_changed (const PBD::PropertyChange&, Mackie::RouteSignal *);
-       void notify_panner_changed (Mackie::RouteSignal *, bool force_update = true);
+       void handle_button_event (Mackie::Surface&, Mackie::Button& button, Mackie::ButtonState);
+
        void notify_route_added (ARDOUR::RouteList &);
-       void notify_active_changed (Mackie::RouteSignal *);
        void notify_remote_id_changed();
 
        /// rebuild the current bank. Called on route added/removed and
        /// remote id changed.
        void refresh_current_bank();
 
-  public:
        // button-related signals
        void notify_record_state_changed();
        void notify_transport_state_changed();
@@ -134,7 +126,7 @@ class MackieControlProtocol
        void update_timecode_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);
+       void update_led(Mackie::Surface&, Mackie::Button & button, Mackie::LedState);
   
        void update_global_button(const std::string & name, Mackie::LedState);
        void update_global_led(const std::string & name, Mackie::LedState);
@@ -280,30 +272,17 @@ class MackieControlProtocol
        Mackie::LedState fader_touch_press (Mackie::Button &);
        Mackie::LedState fader_touch_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; }
  
-       void add_in_use_timeout (Mackie::SurfacePort& port, Mackie::Control& in_use_control, Mackie::Control* touch_control);
+       void add_in_use_timeout (Mackie::Surface& surface, Mackie::Control& in_use_control, Mackie::Control* touch_control);
        
   protected:
-       // create instances of MackiePort, depending on what's found in ardour.rc
-       void create_ports();
-  
        // shut down the surface
        void close();
   
-       // create the Surface object, with the correct number
-       // of strips for the currently connected ports and 
-       // hook up the control event notification
-       void initialize_surface();
-  
        // This sets up the notifications and sets the
        // controls to the correct values
-       void update_surface();
+       void update_surfaces();
        
        // connects global (not strip) signals from the Session to here
        // so the surface can be notified of changes from the other UIs.
@@ -320,54 +299,16 @@ class MackieControlProtocol
        Sorted get_sorted_routes();
   
        // bank switching
-       void switch_banks(int initial);
-       void prev_track();
-       void next_track();
+       void switch_banks (uint32_t first_remote_id, bool force = false);
+       void prev_track ();
+       void next_track ();
   
-       // delete all RouteSignal objects connecting Routes to Strips
-       void clear_route_signals();
-       
-       typedef std::vector<Mackie::RouteSignal*> RouteSignals;
-       RouteSignals route_signals;
-       Glib::Mutex route_signals_lock; 
-
-       // return which of the ports a particular route_table
-       // index belongs to
-       Mackie::MackiePort & port_for_id(uint32_t index);
-
-       /**
-          Handle a button press for the control and return whether
-          the corresponding light should be on or off.
-       */
-       bool handle_strip_button (Mackie::SurfacePort &, Mackie::Control &, Mackie::ButtonState, boost::shared_ptr<ARDOUR::Route>);
-
-       void add_port (MIDI::Port &, MIDI::Port &, int number, Mackie::MackiePort::port_type_t);
-
-       // 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 (ARDOUR::framepos_t now_frame);
        std::string format_timecode_timecode (ARDOUR::framepos_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
-          for new data.
-       */
-       void handle_port_init(Mackie::SurfacePort *);
-
-       /// notification from a MackiePort that it's now active
-       void handle_port_active(Mackie::SurfacePort *);
-       
-       /// notification from a MackiePort that it's now inactive
-       void handle_port_inactive(Mackie::SurfacePort *);
-       
-       boost::shared_ptr<ARDOUR::Route> master_route();
-       Mackie::Strip & master_strip();
-
        void do_request (MackieControlUIRequest*);
        int stop ();
 
@@ -375,23 +316,13 @@ class MackieControlProtocol
 
   private:
 
+       void create_surfaces ();
        void port_connected_or_disconnected (std::string, std::string, bool);
-       bool control_in_use_timeout (Mackie::SurfacePort*, Mackie::Control *, Mackie::Control *);
+       bool control_in_use_timeout (Mackie::Surface*, Mackie::Control *, Mackie::Control *);
 
        bool periodic();
        sigc::connection periodic_connection;
 
-       boost::shared_ptr<Mackie::RouteSignal> master_route_signal;
-       
-       static const char * default_port_name;
-       
-       /// The Midi port(s) connected to the units
-       typedef std::vector<Mackie::MackiePort*> MackiePorts;
-       MackiePorts _ports;
-  
-       /// Sometimes the real port goes away, and we want to contain the breakage
-       Mackie::DummyPort _dummy_port;
-  
        /// The initial remote_id of the currently switched in bank.
        uint32_t _current_initial_bank;
        
@@ -403,16 +334,11 @@ class MackieControlProtocol
        PBD::ScopedConnectionList port_connections;
        PBD::ScopedConnectionList route_connections;
        
-       /// The representation of the physical controls on the surface.
-       Mackie::Surface * _surface;
-       
        bool _transport_previously_rolling;
        
        // timer for two quick marker left presses
        Mackie::Timer _frm_left_last;
        
-       Mackie::JogWheel _jog_wheel;
-       
        // last written timecode string
        std::string _timecode_last;
        
@@ -437,6 +363,8 @@ class MackieControlProtocol
        static const int MODIFIER_CMDALT;
 
        int  _modifier_state;
+
+       Mackie::MackieMidiBuilder builder;
 };
 
 #endif // ardour_mackie_control_protocol_h
index 06aafc59654f42174915e13ca2b74b3a746f7133..66c80c9a8b2473f02d6ff5deeec2f0cc9fd54573 100644 (file)
@@ -25,57 +25,3 @@ using namespace std;
 using namespace Mackie;
 using namespace PBD;
 
-const char * MackieControlProtocol::default_port_name = "mcu";
-
-bool MackieControlProtocol::probe()
-{
-       return true;
-}
-
-void MackieControlProtocol::handle_port_inactive( SurfacePort * port )
-{
-       // port gone away. So stop polling it ASAP
-       {
-               // delete the port instance
-               Glib::Mutex::Lock lock( update_mutex );
-               MackiePorts::iterator it = find( _ports.begin(), _ports.end(), port );
-               if ( it != _ports.end() )
-               {
-                       delete *it;
-                       _ports.erase( it );
-               }
-       }
-
-       // TODO all the rebuilding of surfaces and so on
-}
-
-void MackieControlProtocol::handle_port_active (SurfacePort *)
-{
-       // no need to re-add port because it was already added
-       // during the init phase. So just update the local surface
-       // representation and send the representation to 
-       // all existing ports
-       
-       // TODO update bank size
-       
-       // TODO rebuild surface, to have new units
-       
-       // finally update session state to the surface
-       // TODO but this is also done in set_active, and
-       // in fact update_surface won't execute unless
-#ifdef DEBUG
-       cout << "update_surface in handle_port_active" << endl;
-#endif
-       // _active == true
-       update_surface();
-}
-
-void MackieControlProtocol::handle_port_init (Mackie::SurfacePort *)
-{
-#ifdef DEBUG
-       cout << "MackieControlProtocol::handle_port_init" << endl;
-#endif
-#ifdef DEBUG
-       cout << "MackieControlProtocol::handle_port_init finish" << endl;
-#endif
-}
index 95ab97c5f7913ef9730ed985e2512c919ce70ebb..8439c68fe7078054b6e6fd5a4fd74c679c011631 100644 (file)
 using namespace Mackie;
 using std::isnan;
 
-JogWheel::JogWheel( MackieControlProtocol & mcp )
-: _mcp( mcp )
-, _transport_speed( 4.0 )
-, _transport_direction( 0 )
-, _shuttle_speed( 0.0 )
+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() )
+       if  (!_jog_wheel_states.empty())
                return _jog_wheel_states.top();
        else
                return scroll;
@@ -49,28 +49,28 @@ void JogWheel::scroll_event (SurfacePort &, Control &, const ControlState &)
 void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state)
 {
        // TODO use current snap-to setting?
-       switch ( jog_wheel_state() )
+       switch  (jog_wheel_state())
        {
        case scroll:
-               _mcp.ScrollTimeline( state.delta * state.sign );
+               _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();
+               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();
+                       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() );
+               _transport_speed += _mcp.surfaces.front()->scaled_delta (state, _mcp.get_session().transport_speed());
 
                // make sure no weirdness gets to the session
-               if ( _transport_speed < 0 || isnan( _transport_speed ) )
+               if  (_transport_speed < 0 || isnan (_transport_speed))
                {
                        _transport_speed = 0.0;
                }
@@ -81,11 +81,11 @@ void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state)
        
        case scrub:
        {
-               if ( state.sign != 0 )
+               if  (state.sign != 0)
                {
-                       add_scrub_interval( _scrub_timer.restart() );
+                       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;
+                       float speed = _mcp.surfaces.front()->scrub_scaling_factor() / average_scrub_interval() * state.ticks;
                        _mcp.get_session().request_transport_speed_nonzero (speed * state.sign);
                }
                else
@@ -98,7 +98,7 @@ void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state)
        
        case shuttle:
                _shuttle_speed = _mcp.get_session().transport_speed();
-               _shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
+               _shuttle_speed += _mcp.surfaces.front()->scaled_delta (state, _mcp.get_session().transport_speed());
                _mcp.get_session().request_transport_speed_nonzero (_shuttle_speed);
                break;
        
@@ -111,21 +111,21 @@ void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state)
 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() )
+       if  (!_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval())
        {
-               _mcp.get_session().request_transport_speed( 0.0 );
+               _mcp.get_session().request_transport_speed (0.0);
                _scrub_intervals.clear();
        }
 }
 
-void JogWheel::push( State state )
+void JogWheel::push (State state)
 {
-       _jog_wheel_states.push( state );
+       _jog_wheel_states.push (state);
 }
 
 void JogWheel::pop()
 {
-       if ( _jog_wheel_states.size() > 0 )
+       if  (_jog_wheel_states.size() > 0)
        {
                _jog_wheel_states.pop();
        }
@@ -133,23 +133,23 @@ void JogWheel::pop()
 
 void JogWheel::zoom_state_toggle()
 {
-       if ( jog_wheel_state() == zoom )
+       if  (jog_wheel_state() == zoom)
                pop();
        else
-               push( zoom );
+               push (zoom);
 }
 
 JogWheel::State JogWheel::scrub_state_cycle()
 {
        State top = jog_wheel_state();
-       if ( top == scrub )
+       if  (top == scrub)
        {
                // stop scrubbing and go to shuttle
                pop();
-               push( shuttle );
+               push (shuttle);
                _shuttle_speed = 0.0;
        }
-       else if ( top == shuttle )
+       else if  (top == shuttle)
        {
                // default to scroll, or the last selected
                pop();
@@ -157,25 +157,25 @@ JogWheel::State JogWheel::scrub_state_cycle()
        else
        {
                // start with scrub
-               push( scrub );
+               push (scrub);
        }
        
        return jog_wheel_state();
 }
 
-void JogWheel::add_scrub_interval( unsigned long elapsed )
+void JogWheel::add_scrub_interval (unsigned long elapsed)
 {
-       if ( _scrub_intervals.size() > 5 )
+       if  (_scrub_intervals.size() > 5)
        {
                _scrub_intervals.pop_front();
        }
-       _scrub_intervals.push_back( elapsed );
+       _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 )
+       for  (std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it)
        {
                sum += *it;
        }
@@ -188,9 +188,9 @@ float JogWheel::std_dev_scrub_interval()
        
        // calculate standard deviation
        float sum = 0.0;
-       for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
+       for  (std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it)
        {
-               sum += pow( *it - average, 2 );
+               sum += pow (*it - average, 2);
        }
-       return sqrt( sum / _scrub_intervals.size() -1 );
+       return sqrt (sum / _scrub_intervals.size() -1);
 }
index 714180112d9fa664402a887e02eefb51107c40e2..a57725baa52f6fa92663e2655bb18ad356eec90c 100644 (file)
@@ -39,6 +39,7 @@
 #include "meter.h"
 #include "midi_byte_array.h"
 #include "mackie_port.h"
+#include "surface.h"
 
 using namespace PBD;
 using namespace Mackie;
@@ -77,7 +78,7 @@ MidiByteArray MackieMidiBuilder::build_led_ring (const LedRing & led_ring, const
                               // the control type
                               , midi_pot_id
                               // the id
-                              , 0x20 + led_ring.control_id()
+                              , 0x20 + led_ring.raw_id()
                               // the value
                               , calculate_pot_value (mode, state)
                );
@@ -101,7 +102,7 @@ MidiByteArray MackieMidiBuilder::build_led (const Led & led, LedState ls)
        
        return MidiByteArray  (3
                , midi_button_id
-               , led.control_id()
+               , led.raw_id()
                , state
        );
 }
@@ -111,7 +112,7 @@ MidiByteArray MackieMidiBuilder::build_fader (const Fader & fader, float pos)
        int posi = int (0x3fff * pos);
        
        return MidiByteArray  (3
-                              , midi_fader_id | fader.control_id()
+                              , midi_fader_id | fader.raw_id()
                               // lower-order bits
                               , posi & 0x7f
                               // higher-order bits
@@ -119,7 +120,7 @@ MidiByteArray MackieMidiBuilder::build_fader (const Fader & fader, float pos)
                );
 }
 
-MidiByteArray MackieMidiBuilder::zero_strip (SurfacePort & port, const Strip & strip)
+MidiByteArray MackieMidiBuilder::zero_strip (Surface& surface, const Strip & strip)
 {
        Group::Controls::const_iterator it = strip.controls().begin();
        MidiByteArray retval;
@@ -134,8 +135,8 @@ MidiByteArray MackieMidiBuilder::zero_strip (SurfacePort & port, const Strip & s
 
        /* XXX: not sure about this check to only display stuff for strips of index < 8 */
        if (strip.index() < 8) {
-               retval << strip_display_blank (port, strip, 0);
-               retval << strip_display_blank (port, strip, 1);
+               retval << strip_display_blank (surface, strip, 0);
+               retval << strip_display_blank (surface, strip, 1);
        }
        
        return retval;
@@ -202,23 +203,23 @@ MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std
        return two_char_display (os.str());
 }
 
-MidiByteArray MackieMidiBuilder::strip_display_blank (SurfacePort & port, const Strip & strip, unsigned int line_number)
+MidiByteArray MackieMidiBuilder::strip_display_blank (Surface& surface, 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, "      ");
+       return strip_display (surface, strip, line_number, "      ");
 }
 
-MidiByteArray MackieMidiBuilder::strip_display (SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line)
+MidiByteArray MackieMidiBuilder::strip_display (Surface& surface, const Strip & strip, unsigned int line_number, const std::string & line)
 {
        assert (line_number <= 1);
 
        MidiByteArray retval;
-       uint32_t index = strip.index() % port.strips();
+       uint32_t index = strip.index() % 8;
 
        DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display index: %1, line %2 = %3\n", strip.index(), line_number, line));
 
        // sysex header
-       retval << port.sysex_hdr();
+       retval << surface.sysex_hdr();
        
        // code for display
        retval << 0x12;
@@ -254,7 +255,8 @@ MidiByteArray MackieMidiBuilder::all_strips_display (SurfacePort & /*port*/, std
        return retval;
 }
 
-MidiByteArray MackieMidiBuilder::timecode_display (SurfacePort & port, const std::string & timecode, const std::string & last_timecode)
+MidiByteArray 
+MackieMidiBuilder::timecode_display (Surface& surface, 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();
@@ -278,7 +280,7 @@ MidiByteArray MackieMidiBuilder::timecode_display (SurfacePort & port, const std
        MidiByteArray retval;
        
        // sysex header
-       retval << port.sysex_hdr();
+       retval << surface.sysex_hdr();
        
        // code for timecode display
        retval << 0x10;
index 1b33c75913e1f3207c849c363fd23dc71cc5b205..e4f929a58854c472077b23ecc9a1dd35fe692bdf 100644 (file)
@@ -41,69 +41,72 @@ class LedRing;
 class MackieMidiBuilder
 {
 public:
+       MackieMidiBuilder () {}
+       ~MackieMidiBuilder() {}
+
        /**
                The first byte of a midi message from the surface
                will contain one of these, sometimes bitmasked
                with the control id
        */
        enum midi_types {
-               midi_fader_id = Control::type_fader
-               , midi_button_id = Control::type_button
-               , midi_pot_id = Control::type_pot
+               midi_fader_id = Control::type_fader,
+               midi_button_id = Control::type_button,
+               midi_pot_id = Control::type_pot,
        };
 
        /**
                The LED rings have these modes.
        */
        enum midi_pot_mode {
-               midi_pot_mode_dot = 0
-               , midi_pot_mode_boost_cut = 1
-               , midi_pot_mode_wrap = 2
-               , midi_pot_mode_spread = 3
+               midi_pot_mode_dot = 0,
+               midi_pot_mode_boost_cut = 1,
+               midi_pot_mode_wrap = 2,
+               midi_pot_mode_spread = 3,
        };
 
-       MidiByteArray build_led_ring( const Pot & pot, const ControlState &, 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_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 );
+       MidiByteArray build_led (const Led & led, LedState ls);
+       MidiByteArray build_led (const Button & button, LedState ls);
        
-       MidiByteArray build_fader( const Fader & fader, float pos );
+       MidiByteArray build_fader (const Fader & fader, float pos);
 
        /// return bytes that will reset all controls to their zero positions
-       /// And blank the display for the strip. Pass SurfacePort so we know which sysex header to use.
-       MidiByteArray zero_strip( SurfacePort &, const Strip & strip );
+       /// And blank the display for the strip. Pass Surface so we know which sysex header to use.
+       MidiByteArray zero_strip (Surface&, const Strip & strip);
        
        // provide bytes to zero the given control
-       MidiByteArray zero_control( const Control & control );
+       MidiByteArray zero_control (const Control & control);
        
        // display the first 2 chars of the msg in the 2 char display
        // . is appended to the previous character, so A.B. would
        // be two characters
-       MidiByteArray two_char_display( const std::string & msg, const std::string & dots = "  " );
-       MidiByteArray two_char_display( unsigned int value, const std::string & dots = "  " );
+       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 = "" );
+       MidiByteArray timecode_display (Surface&, 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 );
+       MidiByteArray strip_display (Surface &, 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 );
+       /// blank the strip LCD, ie write all spaces. Pass Surface so we know which sysex header to use.
+       MidiByteArray strip_display_blank (Surface&, 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 );
+       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 & );
+       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
deleted file mode 100644 (file)
index 8c967b7..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
-       Copyright (C) 2006,2007 John Anderson
-
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <sstream>
-
-#include <glibmm/main.h>
-#include <boost/shared_array.hpp>
-
-#include "mackie_port.h"
-
-#include "mackie_control_exception.h"
-#include "mackie_control_protocol.h"
-#include "mackie_midi_builder.h"
-#include "controls.h"
-#include "surface.h"
-
-#include "fader.h"
-#include "button.h"
-#include "strip.h"
-#include "pot.h"
-#include "control_group.h"
-
-#include "midi++/types.h"
-#include "midi++/port.h"
-
-#include "ardour/debug.h"
-#include "ardour/rc_configuration.h"
-
-#include "i18n.h"
-
-using namespace std;
-using namespace Mackie;
-using namespace ARDOUR;
-using namespace PBD;
-
-// The MCU sysex header
-MidiByteArray mackie_sysex_hdr  (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10);
-
-// The MCU extender sysex header
-MidiByteArray mackie_sysex_hdr_xt  (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11);
-
-MackiePort::MackiePort (MackieControlProtocol & mcp, MIDI::Port & input_port, MIDI::Port & output_port, int number, port_type_t port_type)
-       : SurfacePort (input_port, output_port, number)
-       , _mcp (mcp)
-       , _port_type (port_type)
-       , _emulation (none)
-       , _initialising (true)
-       , _connected (false)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::MackiePort\n");
-}
-
-MackiePort::~MackiePort()
-{
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::~MackiePort\n");
-       close();
-       DEBUG_TRACE (DEBUG::MackieControl, "~MackiePort finished\n");
-}
-
-int MackiePort::strips() const
-{
-       if  (_port_type == mcu)
-       {
-               switch  (_emulation)
-               {
-                       // BCF2000 only has 8 faders, so reserve one for master
-                       case bcf2000: return 7;
-                       case mackie: return 8;
-                       case none:
-                       default:
-                               throw MackieControlException ("MackiePort::strips: don't know what emulation we're using");
-               }
-       }
-       else
-       {
-               // must be an extender, ie no master fader
-               return 8;
-       }
-}
-
-// should really be in MackiePort
-void MackiePort::open()
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::open %1\n", *this));
-       
-       input_port().parser()->sysex.connect_same_thread (sysex_connection, boost::bind (&MackiePort::handle_midi_sysex, this, _1, _2, _3));
-                    
-       // make sure the device is connected
-       init();
-}
-
-void MackiePort::close()
-{
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::close\n");
-       
-       // disconnect signals
-
-       sysex_connection.disconnect();
-       ScopedConnectionList::drop_connections ();
-       _connected = false;
-
-       // TODO emit a "closing" signal?
-}
-
-const MidiByteArray & MackiePort::sysex_hdr() const
-{
-       switch  (_port_type)
-       {
-               case mcu: return mackie_sysex_hdr;
-               case ext: return mackie_sysex_hdr_xt;
-       }
-       cout << "MackiePort::sysex_hdr _port_type not known" << endl;
-       return mackie_sysex_hdr;
-}
-
-MidiByteArray calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
-{
-       MidiByteArray l;
-       back_insert_iterator<MidiByteArray> back  (l);
-       copy (begin, end, back);
-       
-       MidiByteArray retval;
-       
-       // this is how to calculate the response to the challenge.
-       // from the Logic docs.
-       retval <<  (0x7f &  (l[0] +  (l[1] ^ 0xa) - l[3]));
-       retval <<  (0x7f &  ( (l[2] >> l[3]) ^  (l[0] + l[3])));
-       retval <<  (0x7f &  ((l[3] -  (l[2] << 2)) ^  (l[0] | l[1])));
-       retval <<  (0x7f &  (l[1] - l[2] +  (0xf0 ^  (l[3] << 4))));
-       
-       return retval;
-}
-
-// not used right now
-MidiByteArray MackiePort::host_connection_query (MidiByteArray & bytes)
-{
-       MidiByteArray response;
-
-       // handle host connection query
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
-       
-       if  (bytes.size() != 18) {
-               finalise_init (false);
-               cerr << "expecting 18 bytes, read " << bytes << " from " << input_port().name() << endl;
-               return response;
-       }
-
-       // build and send host connection reply
-       response << 0x02;
-       copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
-       response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
-       return response;
-}
-
-// not used right now
-MidiByteArray MackiePort::host_connection_confirmation (const MidiByteArray & bytes)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
-       
-       // decode host connection confirmation
-       if  (bytes.size() != 14) {
-               finalise_init (false);
-               ostringstream os;
-               os << "expecting 14 bytes, read " << bytes << " from " << input_port().name();
-               throw MackieControlException (os.str());
-       }
-       
-       // send version request
-       return MidiByteArray (2, 0x13, 0x00);
-}
-
-void MackiePort::probe_emulation (const MidiByteArray &)
-{
-#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;
-               return;
-       }
-
-       finalise_init (true);
-}
-
-void MackiePort::init()
-{
-       DEBUG_TRACE (DEBUG::MackieControl,  "MackiePort::init\n");
-
-       init_mutex.lock();
-       _initialising = true;
-
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::init lock acquired\n");
-
-       // emit pre-init signal
-       init_event();
-       
-       // kick off initialisation. See docs in header file for init()
-       
-       // bypass the init sequence because sometimes the first
-       // message doesn't get to the unit, and there's no way
-       // to do a timed lock in Glib.
-       //write_sysex  (MidiByteArray  (2, 0x13, 0x00));
-       
-       finalise_init (true);
-}
-
-void MackiePort::finalise_init (bool yn)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init\n");
-
-       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;
-                       emulation_ok = true;
-               } else if  (ARDOUR::Config->get_mackie_emulation() == "mcu")  {
-                       _emulation = mackie;
-                       emulation_ok = true;
-               } else {
-                       cout << "unknown mackie emulation: " << ARDOUR::Config->get_mackie_emulation() << endl;
-                       emulation_ok = false;
-               }
-       }
-       
-       yn = yn && emulation_ok;
-       
-       SurfacePort::active (yn);
-
-       if (yn) {
-               active_event();
-               
-               // start handling messages from controls
-               connect_to_signals ();
-       }
-
-       _initialising = false;
-       init_cond.signal();
-       init_mutex.unlock();
-
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init lock released\n");
-}
-
-void MackiePort::connect_to_signals ()
-{
-       if (!_connected) {
-
-               MIDI::Parser* p = input_port().parser();
-
-               /* V-Pot messages are Controller */
-               p->controller.connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_controller_message, this, _1, _2));
-               /* Button messages are NoteOn */
-               p->note_on.connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_note_on_message, this, _1, _2));
-               /* Fader messages are Pitchbend */
-               p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 0U));
-               p->channel_pitchbend[1].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 1U));
-               p->channel_pitchbend[2].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 2U));
-               p->channel_pitchbend[3].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 3U));
-               p->channel_pitchbend[4].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 4U));
-               p->channel_pitchbend[5].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 5U));
-               p->channel_pitchbend[6].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 6U));
-               p->channel_pitchbend[7].connect_same_thread (*this, boost::bind (&MackiePort::handle_midi_pitchbend_message, this, _1, _2, 7U));
-               
-               _connected = true;
-       }
-}
-
-bool MackiePort::wait_for_init()
-{
-       Glib::Mutex::Lock lock (init_mutex);
-       while (_initialising) {
-               DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active waiting\n");
-               init_cond.wait (init_mutex);
-               DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active released\n");
-       }
-       DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active returning\n");
-       return SurfacePort::active();
-}
-
-void MackiePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
-{
-       MidiByteArray bytes (count, raw_bytes);
-
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
-
-       switch (bytes[5])
-       {
-               case 0x01:
-                       write_sysex (host_connection_query (bytes));
-                       break;
-               case 0x03:
-                       // not used right now
-                       write_sysex (host_connection_confirmation (bytes));
-                       break;
-               case 0x04:
-                       inactive_event ();
-                       cout << "host connection error" << bytes << endl;
-                       break;
-               case 0x14:
-                       probe_emulation (bytes);
-                       break;
-               default:
-                       cout << "unknown sysex: " << bytes << endl;
-       }
-}
-
-void
-MackiePort::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi pitchbend on port %3 (number %4), fader = %1 value = %2\n", 
-                                                          (8*number()) + fader_id, pb, *this, number()));
-
-       Control* control = _mcp.surface().faders[(8*number()) + fader_id];
-
-       if (control) {
-               float midi_pos = pb >> 4; // only the top 10 bytes are used
-               _mcp.handle_control_event (*this, *control, midi_pos / 1023.0);
-       } else {
-               DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
-       }
-}
-
-void 
-MackiePort::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::handle_note_on %1 = %2\n", ev->note_number, ev->velocity));
-
-       Control* control = _mcp.surface().buttons[(8*number()) + ev->note_number];
-
-       if (control) {
-               ControlState control_state (ev->velocity == 0x7f ? press : release);
-               control->set_in_use (control_state.button_state == press);
-               control_event (*this, *control, control_state);
-       } else {
-               DEBUG_TRACE (DEBUG::MackieControl, "button not found\n");
-       }
-}
-
-void 
-MackiePort::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::handle_midi_controller %1 = %2\n", ev->controller_number, ev->value));
-
-       Control* control = _mcp.surface().pots[(8*number()) + ev->controller_number];
-
-       if (!control && ev->controller_number == Control::jog_base_id) {
-               control = _mcp.surface().controls_by_name["jog"];
-       }
-
-       if (control) {
-               ControlState state;
-               
-               // bytes[2] & 0b01000000 (0x40) give sign
-               state.sign = (ev->value & 0x40) == 0 ? 1 : -1; 
-               // bytes[2] & 0b00111111 (0x3f) gives delta
-               state.ticks = (ev->value & 0x3f);
-               if (state.ticks == 0) {
-                       /* euphonix and perhaps other devices send zero
-                          when they mean 1, we think.
-                       */
-                       state.ticks = 1;
-               }
-               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.
-               */
-               
-               control->set_in_use (true);
-               _mcp.add_in_use_timeout (*this, *control, control);
-
-               control_event (*this, *control, state);
-       } else {
-               DEBUG_TRACE (DEBUG::MackieControl, "pot not found\n");
-       }
-}
-
-void
-MackiePort::control_event (SurfacePort& sp, Control& c, const ControlState& cs)
-{
-       _mcp.handle_control_event (sp, c, cs);
-}
diff --git a/libs/surfaces/mackie/mackie_port.h b/libs/surfaces/mackie/mackie_port.h
deleted file mode 100644 (file)
index 1b6b77d..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
-       Copyright (C) 2006,2007 John Anderson
-
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-#ifndef mackie_port_h
-#define mackie_port_h
-
-#include <midi++/types.h>
-#include <glibmm/thread.h>
-
-#include "pbd/signals.h"
-
-#include "surface_port.h"
-#include "midi_byte_array.h"
-#include "types.h"
-
-namespace MIDI {
-       class Port;
-       class Parser;
-}
-
-class MackieControlProtocol;
-
-namespace Mackie
-{
-
-class MackiePort : public SurfacePort
-{
-public:
-       enum port_type_t { mcu, ext };
-       enum emulation_t { none, mackie, bcf2000 };
-       
-       MackiePort (MackieControlProtocol & mcp, MIDI::Port & input_port, MIDI::Port & output_port, int number, port_type_t = mcu);
-       ~MackiePort();
-
-       virtual void open();
-       virtual void close();
-
-       /// MCU and extenders have different sysex headers
-       virtual const MidiByteArray & sysex_hdr() const;
-
-       /// Handle device initialisation
-       void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t count );
-       void handle_midi_pitchbend_message (MIDI::Parser &, MIDI::pitchbend_t, uint32_t channel_id);
-       void handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes*);
-       void handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes*);
-       
-       /// return the number of strips associated with this port
-       virtual int strips() const;
-
-       /// Block until the port has finished initialising, and then return
-       /// whether the intialisation succeeded
-       bool wait_for_init();
-       
-       emulation_t emulation() const { return _emulation; }
-       
-       /// Connect the any signal from the parser to handle_midi_any
-       /// unless it's already connected
-       void connect_to_signals ();
-
-protected:
-       /**
-               The initialisation sequence is fairly complex. First a lock is acquired
-               so that a condition can be used to signal the end of the init process.
-               Then a sysex is sent to the device. The response to the sysex
-               is handled by a switch in handle_midi_sysex which calls one of the
-               other methods.
-               
-               However, windows DAWs ignore the documented init sequence and so we
-               do too. Thanks to Essox for helping with this.
-               
-               So we use the version firmware to figure out what device is on
-               the other end of the cable.
-       */
-       void init();
-
-       /**
-               Once the device is initialised, finalise_init(true) is called, which
-               releases the lock and signals the condition, and starts handling incoming
-               messages. finalise_init(false) will also release the lock but doesn't
-               start handling messages.
-       */
-       void finalise_init( bool yn );
-
-       MidiByteArray host_connection_query( MidiByteArray & bytes );
-       MidiByteArray host_connection_confirmation( const MidiByteArray & bytes );
-
-       /**
-               Will set _emulation to what it thinks is correct, based
-               on responses from the device. Or get/set parameters. Or
-               environment variables. Or existence of a file.
-       */
-       void probe_emulation( const MidiByteArray & bytes );
-
-       void control_event (SurfacePort &, Control &, const ControlState &);
-
-private:
-       MackieControlProtocol & _mcp;
-       port_type_t _port_type;
-       PBD::ScopedConnection any_connection;
-       PBD::ScopedConnection sysex_connection;
-       emulation_t _emulation;
-
-       bool _initialising;
-       bool _connected;
-       Glib::Cond init_cond;
-       Glib::Mutex init_mutex;
-};
-
-}
-
-#endif
diff --git a/libs/surfaces/mackie/mackie_surface.cc b/libs/surfaces/mackie/mackie_surface.cc
deleted file mode 100644 (file)
index 415d4b1..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <cmath>
-#include <sstream>
-#include <string>
-#include <cstdio>
-
-#include "controls.h"
-#include "mackie_surface.h"
-#include "mackie_midi_builder.h"
-#include "surface_port.h"
-
-using namespace Mackie;
-
-void 
-MackieSurface::display_timecode (SurfacePort & port, MackieMidiBuilder & builder, const std::string & timecode, const std::string & timecode_last)
-{
-       port.write (builder.timecode_display (port, timecode, timecode_last));
-}
-
-float 
-MackieSurface::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/mackie_surface.h b/libs/surfaces/mackie/mackie_surface.h
deleted file mode 100644 (file)
index 18fcab5..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef mackie_surface_mackie_h
-#define mackie_surface_mackie_h
-/*
-       Generated by scripts/generate-surface.rb
-*/
-
-#include "surface.h"
-
-namespace Mackie
-{
-
-class MackieButtonHandler;
-class MackieSurface : public Surface
-{
-  public:
-        MackieSurface (uint32_t max_strips) : Surface  (max_strips, 8) {}
-       
-       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);
-};
-
-}
-
-#endif
index 84674cb7afd62ea178ce6b47c0b9689291e92fdf..253b39db82f792a96a57a99149d18e85e62a8dd5 100644 (file)
@@ -25,6 +25,7 @@
 #include "ardour/rc_configuration.h"
 
 #include "mackie_control_protocol.h"
+#include "surface.h"
 
 #include "i18n.h"
 
@@ -89,8 +90,8 @@ LedState
 MackieControlProtocol::left_press (Button &)
 {
        Sorted sorted = get_sorted_routes();
-       if (sorted.size() > route_table.size()) {
-               int new_initial = _current_initial_bank - route_table.size();
+       if (sorted.size() > n_strips()) {
+               int new_initial = _current_initial_bank - n_strips();
                if (new_initial < 0) {
                        new_initial = 0;
                }
@@ -116,11 +117,13 @@ LedState
 MackieControlProtocol::right_press (Button &)
 {
        Sorted sorted = get_sorted_routes();
-       if (sorted.size() > route_table.size()) {
-               uint32_t delta = sorted.size() - (route_table.size() + _current_initial_bank);
+       uint32_t strip_cnt = n_strips();
 
-               if (delta > route_table.size()) {
-                       delta = route_table.size();
+       if (sorted.size() > strip_cnt) {
+               uint32_t delta = sorted.size() - (strip_cnt + _current_initial_bank);
+
+               if (delta > strip_cnt) {
+                       delta = strip_cnt;
                }
                
                if (delta > 0) {
@@ -230,7 +233,7 @@ LedState
 MackieControlProtocol::channel_left_press (Button &)
 {
        Sorted sorted = get_sorted_routes();
-       if (sorted.size() > route_table.size()) {
+       if (sorted.size() > n_strips()) {
                prev_track();
                return on;
        } else {
@@ -248,7 +251,7 @@ LedState
 MackieControlProtocol::channel_right_press (Button &)
 {
        Sorted sorted = get_sorted_routes();
-       if (sorted.size() > route_table.size()) {
+       if (sorted.size() > n_strips()) {
                next_track();
                return on;
        } else {
@@ -505,17 +508,21 @@ MackieControlProtocol::record_release (Button &)
 LedState 
 MackieControlProtocol::rewind_press (Button &)
 {
-       _jog_wheel.push (JogWheel::speed);
-       _jog_wheel.transport_direction (-1);
-       session->request_transport_speed (-_jog_wheel.transport_speed());
+       JogWheel* jog = surfaces.front()->jog_wheel();
+       assert (jog);
+       jog->push (JogWheel::speed);
+       jog->transport_direction (-1);
+       session->request_transport_speed (-jog->transport_speed());
        return on;
 }
 
 LedState 
 MackieControlProtocol::rewind_release (Button &)
 {
-       _jog_wheel.pop();
-       _jog_wheel.transport_direction (0);
+       JogWheel* jog = surfaces.front()->jog_wheel();
+       assert (jog);
+       jog->pop();
+       jog->transport_direction (0);
        if (_transport_previously_rolling) {
                session->request_transport_speed (1.0);
        } else {
@@ -527,17 +534,21 @@ MackieControlProtocol::rewind_release (Button &)
 LedState 
 MackieControlProtocol::ffwd_press (Button &)
 {
-       _jog_wheel.push (JogWheel::speed);
-       _jog_wheel.transport_direction (1);
-       session->request_transport_speed (_jog_wheel.transport_speed());
+       JogWheel* jog = surfaces.front()->jog_wheel();
+       assert (jog);
+       jog->push (JogWheel::speed);
+       jog->transport_direction (1);
+       session->request_transport_speed (jog->transport_speed());
        return on;
 }
 
 LedState 
 MackieControlProtocol::ffwd_release (Button &)
 {
-       _jog_wheel.pop();
-       _jog_wheel.transport_direction (0);
+       JogWheel* jog = surfaces.front()->jog_wheel();
+       assert (jog);
+       jog->pop();
+       jog->transport_direction (0);
        if (_transport_previously_rolling) {
                session->request_transport_speed (1.0);
        } else {
index a7dbf258314eee23f980df0a1ca3921138d42d57..5bc7c490967626ba11917793e29cee083594d1a5 100644 (file)
@@ -31,9 +31,9 @@ using namespace Mackie;
 using namespace PBD;
 
 Control*
-Meter::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
+Meter::factory (Surface& surface, int id, const char* name, Group& group)
 {
-       Meter* m = new Meter (id, ordinal, name, group);
+       Meter* m = new Meter (id, name, group);
        surface.meters[id] = m;
        surface.controls.push_back (m);
        group.add (*m);
index 70d44e4515b7a3e6b844f83c751ad5f20e7baf8e..7110f044166bc0867ed4e66d5f451433f7a7007b 100644 (file)
@@ -30,8 +30,8 @@ class SurfacePort;
 class Meter : public Control
 {
 public:
-       Meter (int id, int ordinal, std::string name, Group & group)
-               : Control  (id, ordinal, name, group)
+       Meter (int id, std::string name, Group & group)
+               : Control  (id, name, group)
                , last_segment_value_sent (-1)
                , overload_on (false) {}
        
@@ -39,7 +39,7 @@ public:
 
        MidiByteArray update_message (float dB);
 
-       static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
+       static Control* factory (Surface&, int id, const char*, Group&);
        
        int last_segment_value_sent;
 
index 42e5c643e8d70d01955b96687b9c0449441040e1..767ea6e6df9729ff5b16b3cf59d9d1892038381f 100644 (file)
@@ -9,15 +9,15 @@ namespace Mackie {
 class Pot : public Control
 {
 public:
-       Pot (int id, int ordinal, std::string name, Group & group)
-               : Control (id, ordinal, name, group)
-               , _led_ring (id, ordinal, name + "_ring", group) {}
+       Pot (int id, std::string name, Group & group)
+               : Control (id, name, group)
+               , _led_ring (id, name + "_ring", group) {}
 
        virtual type_t type() const { return type_pot; }
 
        virtual const LedRing & led_ring() const {return _led_ring; }
 
-       static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
+       static Control* factory (Surface&, int id, const char*, Group&);
 
 private:
        LedRing _led_ring;
diff --git a/libs/surfaces/mackie/route_signal.cc b/libs/surfaces/mackie/route_signal.cc
deleted file mode 100644 (file)
index f40fff4..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-       Copyright (C) 2006,2007 John Anderson
-
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <stdexcept>
-
-#include "ardour/route.h"
-#include "ardour/track.h"
-#include "ardour/midi_ui.h"
-#include "ardour/pannable.h"
-#include "ardour/session_object.h" // for Properties::name 
-
-#include "mackie_control_protocol.h"
-#include "route_signal.h"
-#include "strip.h"
-
-using namespace ARDOUR;
-using namespace Mackie;
-using namespace std;
-
-#define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
-#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
-
-void RouteSignal::connect()
-{
-       if (_strip.has_solo()) {
-               _route->solo_control()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_solo_changed, &_mcp, this), midi_ui_context());
-       }
-
-       if (_strip.has_mute()) {
-               _route->mute_control()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_mute_changed, &_mcp, this), midi_ui_context());
-       }
-
-       if (_strip.has_gain()) {
-               _route->gain_control()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_gain_changed, &_mcp, this, false), midi_ui_context());
-       }
-
-       _route->PropertyChanged.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_property_changed, &_mcp, _1, this), midi_ui_context());
-       
-       if (_route->pannable()) {
-               _route->pannable()->pan_azimuth_control->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context());
-               _route->pannable()->pan_width_control->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context());
-       }
-       
-       boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
-       if (trk) {
-               trk->rec_enable_control()->Changed .connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_record_enable_changed, &_mcp, this), midi_ui_context());
-       }
-       
-       // TODO this works when a currently-banked route is made inactive, but not
-       // when a route is activated which should be currently banked.
-       _route->active_changed.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_active_changed, &_mcp, this), midi_ui_context());
-
-       _route->DropReferences.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::refresh_current_bank, &_mcp), midi_ui_context());
-       
-       // TODO
-       // SelectedChanged
-       // RemoteControlIDChanged. Better handled at Session level.
-}
-
-void 
-RouteSignal::disconnect()
-{
-       connections.drop_connections ();
-}
-
-void 
-RouteSignal::notify_all()
-{
-       if  (_strip.has_solo()) {
-               _mcp.notify_solo_changed (this);
-       }
-       
-       if  (_strip.has_mute()) {
-               _mcp.notify_mute_changed (this);
-       }
-       
-       if  (_strip.has_gain()) {
-               _mcp.notify_gain_changed (this);
-       }
-       
-       _mcp.notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name), this);
-       
-       if  (_strip.has_vpot()) {
-               _mcp.notify_panner_changed (this);
-       }
-       
-       if  (_strip.has_recenable()) {
-               _mcp.notify_record_enable_changed (this);
-       }
-}
diff --git a/libs/surfaces/mackie/route_signal.h b/libs/surfaces/mackie/route_signal.h
deleted file mode 100644 (file)
index 59bfc66..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
-       Copyright (C) 2006,2007 John Anderson
-
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-#ifndef route_signal_h
-#define route_signal_h
-
-#include <vector>
-#include <boost/shared_ptr.hpp>
-
-#include "pbd/signals.h"
-
-#include "midi_byte_array.h"
-
-class MackieControlProtocol;
-
-namespace ARDOUR {
-       class Route;
-}
-       
-namespace Mackie
-{
-
-class Strip;
-class SurfacePort;
-
-/**
-  This class is intended to easily create and destroy the set of
-  connections from a route to a control surface strip. Instantiating
-  it will connect the signals, and destructing it will disconnect
-  the signals.
-*/
-class RouteSignal
-{
-public:
-        RouteSignal(boost::shared_ptr<ARDOUR::Route> route, MackieControlProtocol & mcp, Strip & strip, SurfacePort & port )
-       : _route( route ), _mcp( mcp ), _strip( strip ), _port( port ), _last_gain_written(0.0)
-       {
-               connect();
-       }
-       
-       ~RouteSignal()
-       {
-               disconnect();
-       }
-       
-       void connect();
-       void disconnect();
-       
-       // call all signal handlers manually
-       void notify_all();
-       
-       boost::shared_ptr<const ARDOUR::Route> route() const { return _route; }
-       Strip & strip() { return _strip; }
-       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:
-       boost::shared_ptr<ARDOUR::Route> _route;
-       MackieControlProtocol & _mcp;
-       Strip & _strip;
-       SurfacePort & _port;
-
-       PBD::ScopedConnectionList connections;
-
-       // 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;
-};
-
-}
-
-#endif
index e20f33e1df2364661c221a2577c9acee66cf36a5..fd9f4299d0eaa89995f634232ea7d5ff3e3dc110 100644 (file)
 #include <stdint.h>
 #include "strip.h"
 
+#include "midi++/port.h"
+
+#include "pbd/compose.h"
+#include "pbd/convert.h"
+
+#include "ardour/debug.h"
+#include "ardour/midi_ui.h"
+#include "ardour/route.h"
+#include "ardour/track.h"
+#include "ardour/pannable.h"
+#include "ardour/panner.h"
+#include "ardour/rc_configuration.h"
+#include "ardour/meter.h"
+
+#include "mackie_control_protocol.h"
+#include "surface.h"
 #include "button.h"
 #include "led.h"
 #include "ledring.h"
 
 using namespace Mackie;
 using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
 
-Strip::Strip (const std::string& name, int index)
-       : Group (name)
-       , _solo (0)
-       , _recenable (0)
-       , _mute (0)
-       , _select (0)
-       , _vselect (0)
-       , _fader_touch (0)
-       , _vpot (0)
-       , _gain (0)
-       , _index (index)
-{
-       /* master strip only */
-}
+#define midi_ui_context() ARDOUR::MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
+#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
+
+extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int);
+#define invalidator(x) __invalidator (*(MidiControlUI::instance()), __FILE__, __LINE__)
 
-Strip::Strip (Surface& surface, const std::string& name, int surface_number, int index, int unit_index, StripControlDefinition* ctls)
+Strip::Strip (Surface& s, const std::string& name, int index, StripControlDefinition* ctls)
        : Group (name)
        , _solo (0)
        , _recenable (0)
@@ -58,16 +67,22 @@ Strip::Strip (Surface& surface, const std::string& name, int surface_number, int
        , _vpot (0)
        , _gain (0)
        , _index (index)
+       , _surface (&s)
 {
        /* build the controls for this track, which will automatically add them
           to the Group 
        */
 
        for (uint32_t i = 0; ctls[i].name[0]; ++i) {
-               ctls[i].factory (surface, ctls[i].base_id + (8*surface_number) + unit_index, unit_index+1, ctls[i].name, *this);
+               ctls[i].factory (*_surface, ctls[i].base_id + index, ctls[i].name, *this);
        }
 }      
 
+Strip::~Strip ()
+{
+       
+}
+
 /**
        TODO could optimise this to use enum, but it's only
        called during the protocol class instantiation.
@@ -206,3 +221,268 @@ std::ostream & Mackie::operator <<  (std::ostream & os, const Strip & strip)
        
        return os;
 }
+
+void
+Strip::set_route (boost::shared_ptr<Route> r)
+{
+       route_connections.drop_connections ();
+
+       _route = r;
+
+       if (r) {
+
+               if (has_solo()) {
+                       _route->solo_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_solo_changed, this), midi_ui_context());
+               }
+               if (has_mute()) {
+                       _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_mute_changed, this), midi_ui_context());
+               }
+               
+               if (has_gain()) {
+                       _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_gain_changed, this, false), midi_ui_context());
+               }
+               
+               _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_property_changed, this, _1), midi_ui_context());
+               
+               if (_route->pannable()) {
+                       _route->pannable()->pan_azimuth_control->Changed.connect(route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_panner_changed, this, false), midi_ui_context());
+                       _route->pannable()->pan_width_control->Changed.connect(route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_panner_changed, this, false), midi_ui_context());
+               }
+               
+               boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
+       
+               if (trk) {
+                       trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_record_enable_changed, this), midi_ui_context());
+               }
+               
+               // TODO this works when a currently-banked route is made inactive, but not
+               // when a route is activated which should be currently banked.
+               
+               _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_active_changed, this), midi_ui_context());
+               _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, ui_bind (&Strip::notify_route_deleted, this), midi_ui_context());
+       
+               // TODO
+               // SelectedChanged
+               // RemoteControlIDChanged. Better handled at Session level.
+
+               /* Update */
+
+               notify_all ();
+       }
+}
+
+void 
+Strip::notify_all()
+{
+       if  (has_solo()) {
+               notify_solo_changed ();
+       }
+       
+       if  (has_mute()) {
+               notify_mute_changed ();
+       }
+       
+       if  (has_gain()) {
+               notify_gain_changed ();
+       }
+       
+       notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
+       
+       if  (has_vpot()) {
+               notify_panner_changed ();
+       }
+       
+       if  (has_recenable()) {
+               notify_record_enable_changed ();
+       }
+}
+
+void 
+Strip::notify_solo_changed ()
+{
+       if (_route) {
+               Button& button = solo();
+               _surface->write (builder.build_led (button, _route->soloed()));
+       }
+}
+
+void 
+Strip::notify_mute_changed ()
+{
+       if (_route) {
+               Button & button = mute();
+               _surface->write (builder.build_led (button, _route->muted()));
+       }
+}
+
+void 
+Strip::notify_record_enable_changed ()
+{
+       if (_route) {
+               Button & button = recenable();
+               _surface->write (builder.build_led (button, _route->record_enabled()));
+       }
+}
+
+void 
+Strip::notify_active_changed ()
+{
+       _surface->mcp().refresh_current_bank();
+}
+
+void 
+Strip::notify_route_deleted ()
+{
+       _surface->mcp().refresh_current_bank();
+}
+
+void 
+Strip::notify_gain_changed (bool force_update)
+{
+       if (_route) {
+               Fader & fader = gain();
+               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("route %1 gain change, update fader %2 on port %3\n", 
+                                                                  _route->name(), 
+                                                                  fader.raw_id(),
+                                                                  _surface->port().output_port().name()));
+               if (!fader.in_use()) {
+                       float gain_value = gain_to_slider_position (_route->gain_control()->get_value());
+                       // check that something has actually changed
+                       if (force_update || gain_value != _last_gain_written) {
+                               _surface->write (builder.build_fader (fader, gain_value));
+                               _last_gain_written = gain_value;
+                       }
+               }
+       }
+}
+
+void 
+Strip::notify_property_changed (const PropertyChange& what_changed)
+{
+       if (!what_changed.contains (ARDOUR::Properties::name)) {
+               return;
+       }
+
+       if (_route) {
+               string line1;
+               string fullname = _route->name();
+               
+               if (fullname.length() <= 6) {
+                       line1 = fullname;
+               } else {
+                       line1 = PBD::short_version (fullname, 6);
+               }
+               
+               _surface->write (builder.strip_display (*_surface, *this, 0, line1));
+               _surface->write (builder.strip_display_blank (*_surface, *this, 1));
+       }
+}
+
+void 
+Strip::notify_panner_changed (bool force_update)
+{
+       if (_route) {
+               Pot & pot = vpot();
+               boost::shared_ptr<Panner> panner = _route->panner();
+               if (panner) {
+                       double pos = panner->position ();
+
+                       // 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 != _last_pan_written)
+                       {
+                               _surface->write (bytes);
+                               _last_pan_written = bytes;
+                       }
+               } else {
+                       _surface->write (builder.zero_control (pot));
+               }
+       }
+}
+
+bool 
+Strip::handle_button (SurfacePort & port, Control & control, ButtonState bs)
+{
+       if (!_route) {
+               // no route so always switch the light off
+               // because no signals will be emitted by a non-route
+               _surface->write (builder.build_led (control.led(), off));
+               return false;
+       }
+
+       bool state = false;
+
+       if (bs == press) {
+               if (control.name() == "recenable") {
+                       state = !_route->record_enabled();
+                       _route->set_record_enabled (state, this);
+               } else if (control.name() == "mute") {
+                       state = !_route->muted();
+                       _route->set_mute (state, this);
+               } else if (control.name() == "solo") {
+                       state = !_route->soloed();
+                       _route->set_solo (state, this);
+               } else if (control.name() == "select") {
+                       _surface->mcp().select_track (_route);
+               } else if (control.name() == "vselect") {
+                       // TODO could be used to select different things to apply the pot to?
+                       //state = default_button_press (dynamic_cast<Button&> (control));
+               }
+       }
+
+       if (control.name() == "fader_touch") {
+
+               state = (bs == press);
+               
+               gain().set_in_use (state);
+               
+               if (ARDOUR::Config->get_mackie_emulation() == "bcf" && state) {
+
+                       /* BCF faders don't support touch, so add a timeout to reset
+                          their `in_use' state.
+                       */
+
+                       _surface->mcp().add_in_use_timeout (*_surface, gain(), &fader_touch());
+               }
+       }
+
+       return state;
+}
+
+void
+Strip::periodic ()
+{
+       if (!_route) {
+               return;
+       }
+
+       update_automation ();
+       update_meter ();
+}
+
+void 
+Strip::update_automation ()
+{
+       ARDOUR::AutoState gain_state = _route->gain_control()->automation_state();
+
+       if (gain_state == Touch || gain_state == Play) {
+               notify_gain_changed (false);
+       }
+
+       if (_route->panner()) {
+               ARDOUR::AutoState panner_state = _route->panner()->automation_state();
+               if (panner_state == Touch || panner_state == Play) {
+                       notify_panner_changed (false);
+               }
+       }
+}
+
+void
+Strip::update_meter ()
+{
+       float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
+       _surface->write (meter().update_message (dB));
+}
index 74928181c757813e0d530fc0e6eec06a923879df..97997e7f0bfc40507c8f6aa4aed2cce5e6401038 100644 (file)
@@ -4,7 +4,14 @@
 #include <string>
 #include <iostream>
 
+#include "pbd/property_basics.h"
+
 #include "control_group.h"
+#include "mackie_midi_builder.h"
+
+namespace ARDOUR {
+       class Route;
+}
 
 namespace Mackie {
 
@@ -18,13 +25,13 @@ class Meter;
 struct StripControlDefinition {
     const char* name;
     uint32_t base_id;
-    Control* (*factory)(Surface&, int index, int ordinal, const char* name, Group&);
+    Control* (*factory)(Surface&, int index, const char* name, Group&);
 };
 
 struct GlobalControlDefinition {
     const char* name;
     uint32_t id;
-    Control* (*factory)(Surface&, int index, int ordinal, const char* name, Group&);
+    Control* (*factory)(Surface&, int index, const char* name, Group&);
     const char* group_name;
 };
 
@@ -34,11 +41,12 @@ struct GlobalControlDefinition {
 class Strip : public Group
 {
 public:
-       Strip (const std::string& name, int index); /* master strip only */
-       Strip (Surface&, const std::string & name, int surface_number, int index, int unit_index, StripControlDefinition* ctls);
+       Strip (Surface&, const std::string & name, int index, StripControlDefinition* ctls);
+       ~Strip();
 
-       virtual bool is_strip() const { return true; }
-       virtual void add (Control & control);
+       boost::shared_ptr<ARDOUR::Route> route() const { return _route; }
+
+       void add (Control & control);
        int index() const { return _index; } // zero based
        
        Button & solo();
@@ -60,6 +68,16 @@ public:
        bool has_vpot() const { return _vpot != 0; }
        bool has_gain() const { return _gain != 0; }
        bool has_meter() const { return _meter != 0; }
+
+       void set_route (boost::shared_ptr<ARDOUR::Route>);
+
+       // call all signal handlers manually
+       void notify_all();
+
+       bool handle_button (SurfacePort & port, Control & control, ButtonState bs);
+
+       void periodic ();
+
 private:
        Button* _solo;
        Button* _recenable;
@@ -71,19 +89,35 @@ private:
        Fader*  _gain;
        Meter*  _meter;
        int     _index;
-};
+       Surface* _surface;
 
-std::ostream & operator <<  (std::ostream &, const Strip &);
+       MackieMidiBuilder builder;
 
-class MasterStrip : public Strip
-{
-public:
-       MasterStrip (const std::string & name, int index)
-               : Strip (name, index) {}
+       boost::shared_ptr<ARDOUR::Route> _route;
+       PBD::ScopedConnectionList route_connections;
+
+       // 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;
+
+
+       void notify_solo_changed ();
+       void notify_mute_changed ();
+       void notify_record_enable_changed ();
+       void notify_gain_changed (bool force_update = true);
+       void notify_property_changed (const PBD::PropertyChange&);
+       void notify_panner_changed (bool force_update = true);
+       void notify_active_changed ();
+       void notify_route_deleted ();
        
-       virtual bool is_master() const  { return true; }
+       void update_automation ();
+       void update_meter ();
+
 };
 
+std::ostream & operator <<  (std::ostream &, const Strip &);
+
 }
 
 #endif /* __ardour_mackie_control_protocol_strip_h__ */
index 9ec426179d36281bf6856b9ca07ecdded2d18290..dda453087d47cfac70440f436eb78baef63311dd 100644 (file)
@@ -2,13 +2,24 @@
 #include <iomanip>
 #include <iostream>
 #include <cstdio>
+#include <cmath>
+
+#include "midi++/port.h"
+#include "midi++/manager.h"
 
 #include "ardour/debug.h"
+#include "ardour/route.h"
+#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
+#include "ardour/rc_configuration.h"
 
 #include "control_group.h"
 #include "surface_port.h"
 #include "surface.h"
 #include "strip.h"
+#include "mackie_midi_builder.h"
+#include "mackie_control_protocol.h"
+#include "mackie_jog_wheel.h"
 
 #include "strip.h"
 #include "button.h"
 #include "jog.h"
 #include "meter.h"
 
+#include "i18n.h"
+
 using namespace std;
 using namespace PBD;
 using namespace Mackie;
+using ARDOUR::Route;
+using ARDOUR::Panner;
+using ARDOUR::Pannable;
+using ARDOUR::PannerShell;
 
-Surface::Surface (uint32_t max_strips, uint32_t unit_strips)
-       : _max_strips (max_strips)
-       , _unit_strips( unit_strips )
-{
-}
+// The MCU sysex header
+static MidiByteArray mackie_sysex_hdr  (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10);
+
+// The MCU extender sysex header
+static MidiByteArray mackie_sysex_hdr_xt  (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11);
 
-void Surface::init ()
+static MidiByteArray empty_midi_byte_array;
+
+Surface::Surface (MackieControlProtocol& mcp, jack_client_t* jack, const std::string& device_name, uint32_t number, surface_type_t stype)
+       : _mcp (mcp)
+       , _stype (stype)
+       , _number (number)
+       , _active (false)
+       , _connected (false)
+       , _jog_wheel (0)
 {
        DEBUG_TRACE (DEBUG::MackieControl, "Surface::init\n");
+       
+       MIDI::Manager * mm = MIDI::Manager::instance();
+       MIDI::Port * input = mm->add_port (new MIDI::Port (string_compose (_("%1 in"), device_name), MIDI::Port::IsInput, jack));
+       MIDI::Port * output = mm->add_port (new MIDI::Port (string_compose (_("%1 out"), device_name), MIDI::Port::IsOutput, jack));
+
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has ports named %1 and %2\n",
+                                                          input->name(), output->name()));
+
+       _port = new SurfacePort (*this, *input, *output);
+       _port->open();
+       _port->inactive_event.connect_same_thread (*this, boost::bind (&Surface::handle_port_inactive, this, _port));
 
-       strips.resize (_max_strips);
-       init_controls ();
-       init_strips ();
+       switch (stype) {
+       case mcu:
+               init_controls ();
+               _jog_wheel = new Mackie::JogWheel (_mcp);
+               break;
+       default:
+               break;
+       }
+
+       switch (stype) {
+       case mcu:
+       case ext:
+               strips.resize (8);
+               init_strips ();
+               break;
+       default:
+               break;
+       }
 
        DEBUG_TRACE (DEBUG::MackieControl, "Surface::init finish\n");
 }
 
 Surface::~Surface ()
 {
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface: destructor\n");
+
+       zero_all ();
+
        // delete groups
        for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
                delete it->second;
@@ -51,6 +106,20 @@ Surface::~Surface ()
        for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
                delete *it;
        }
+       
+       delete _jog_wheel;
+       delete _port;
+}
+
+const MidiByteArray& 
+Surface::sysex_hdr() const
+{
+       switch  (_stype) {
+       case mcu: return mackie_sysex_hdr;
+       case ext: return mackie_sysex_hdr_xt;
+       }
+       cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
+       return mackie_sysex_hdr;
 }
 
 static GlobalControlDefinition mackie_global_controls[] = {
@@ -143,14 +212,11 @@ Surface::init_controls()
        groups["none"] = new Group  ("none");
        groups["transport"] = new Group  ("transport");
        groups["user"] = new Group  ("user");
-
-       group = new MasterStrip  ("master", 0);
-       groups["master"] = group;
-       strips[0] = dynamic_cast<Strip*> (group);
+       groups["master"] = new Group ("master");
 
        for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
                group = groups[mackie_global_controls[n].group_name];
-               Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, 1, mackie_global_controls[n].name, *group);
+               Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
                controls_by_name[mackie_global_controls[n].name] = control;
                group->add (*control);
        }
@@ -172,20 +238,366 @@ static StripControlDefinition mackie_strip_controls[] = {
 void 
 Surface::init_strips ()
 {
-       for (uint32_t i = 0; i < _max_strips; ++i) {
+       for (uint32_t i = 0; i < 8; ++i) {
 
                char name[32];
                
-               uint32_t unit_index = i % _unit_strips;
-               
-               snprintf (name, sizeof (name), "strip_%d", unit_index+1);
+               snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
 
-               cerr << "Register strip " << i << " unit index " << unit_index << endl;
+               cerr << "Register strip " << i << endl;
                
-               Strip* strip = new Strip (*this, name, i/8, i, unit_index, mackie_strip_controls);
+               Strip* strip = new Strip (*this, name, i, mackie_strip_controls);
                
                groups[name] = strip;
                strips[i] = strip;
        }
 }
 
+void 
+Surface::display_timecode (const std::string & timecode, const std::string & timecode_last)
+{
+       if (has_timecode_display()) {
+               _port->write (builder.timecode_display (*this, timecode, timecode_last));
+       }
+}
+
+float 
+Surface::scaled_delta (const ControlState & state, float current_speed)
+{
+       return state.sign *  (std::pow (float(state.ticks + 1), 2) + current_speed) / 100.0;
+}
+
+void 
+Surface::display_bank_start (uint32_t current_bank)
+{
+       if  (current_bank == 0) {
+               // send Ar. to 2-char display on the master
+               _port->write (builder.two_char_display ("Ar", ".."));
+       } else {
+               // write the current first remote_id to the 2-char display
+               _port->write (builder.two_char_display (current_bank));
+       }
+}
+
+void 
+Surface::blank_jog_ring ()
+{
+       Control* control = controls_by_name["jog"];
+
+       if (control) {
+               _port->write (builder.build_led_ring (*(dynamic_cast<Pot*> (control)), off));
+       }
+}
+
+bool 
+Surface::has_timecode_display () const
+{
+       return false;
+}
+
+float
+Surface::scrub_scaling_factor () const
+{
+       return 100.0;
+}
+
+void 
+Surface::connect_to_signals ()
+{
+       if (!_connected) {
+
+               MIDI::Parser* p = _port->input_port().parser();
+
+               /* V-Pot messages are Controller */
+               p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
+               /* Button messages are NoteOn */
+               p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
+               /* Fader messages are Pitchbend */
+               p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 0U));
+               p->channel_pitchbend[1].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 1U));
+               p->channel_pitchbend[2].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 2U));
+               p->channel_pitchbend[3].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 3U));
+               p->channel_pitchbend[4].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 4U));
+               p->channel_pitchbend[5].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 5U));
+               p->channel_pitchbend[6].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 6U));
+               p->channel_pitchbend[7].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 7U));
+               
+               _connected = true;
+       }
+}
+
+
+void
+Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
+{
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi pitchbend on port %3, fader = %1 value = %2\n", 
+                                                          fader_id, pb, _number));
+       
+       Control* control = faders[fader_id];
+
+       if (control) {
+               float midi_pos = pb >> 4; // only the top 10 bytes are used
+               handle_control_event (*control, midi_pos / 1023.0);
+       } else {
+               DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
+       }
+}
+
+void 
+Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
+{
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_note_on %1 = %2\n", ev->note_number, ev->velocity));
+
+       Control* control = buttons[ev->note_number];
+
+       if (control) {
+               ControlState control_state (ev->velocity == 0x7f ? press : release);
+               control->set_in_use (control_state.button_state == press);
+               handle_control_event (*control, control_state);
+       } else {
+               DEBUG_TRACE (DEBUG::MackieControl, "button not found\n");
+       }
+}
+
+void 
+Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
+{
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", ev->controller_number, ev->value));
+
+       Control* control = pots[ev->controller_number];
+
+       if (!control && ev->controller_number == Control::jog_base_id) {
+               control = controls_by_name["jog"];
+       }
+
+       if (control) {
+               ControlState state;
+               
+               // bytes[2] & 0b01000000 (0x40) give sign
+               state.sign = (ev->value & 0x40) == 0 ? 1 : -1; 
+               // bytes[2] & 0b00111111 (0x3f) gives delta
+               state.ticks = (ev->value & 0x3f);
+               if (state.ticks == 0) {
+                       /* euphonix and perhaps other devices send zero
+                          when they mean 1, we think.
+                       */
+                       state.ticks = 1;
+               }
+               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.
+               */
+               
+               control->set_in_use (true);
+               _mcp.add_in_use_timeout (*this, *control, control);
+
+               handle_control_event (*control, state);
+       } else {
+               DEBUG_TRACE (DEBUG::MackieControl, "pot not found\n");
+       }
+}
+
+void 
+Surface::handle_control_event (Control & control, const ControlState & state)
+{
+       // find the route for the control, if there is one
+       boost::shared_ptr<Route> route;
+       Strip* strip;
+
+       if ((strip = dynamic_cast<Strip*> (&control.group())) != 0) {
+               route = strip->route ();
+       }
+
+       // This handles control element events from the surface
+       // the state of the controls on the surface is usually updated
+       // from UI events.
+
+       switch (control.type()) {
+       case Control::type_fader:
+               // 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) {
+                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", state.pos));
+                       
+                       route->gain_control()->set_value (slider_position_to_gain (state.pos));
+                       
+                       if (ARDOUR::Config->get_mackie_emulation() == "bcf") {
+                               /* reset the timeout while we're still moving the fader */
+                               _mcp.add_in_use_timeout (*this, control, control.in_use_touch_control);
+                       }
+                       
+                       // 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;
+               
+       case Control::type_button:
+               if (strip) {
+                       strip->handle_button (*_port, control, state.button_state);
+               } else {
+                       // handle all non-strip buttons
+                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", control.id()));
+                       _mcp.handle_button_event (*this, dynamic_cast<Button&>(control), state.button_state);
+                       
+               }
+               break;
+               
+               // pot (jog wheel, external control)
+       case Control::type_pot:
+               if (strip) {
+                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip pot %1\n", control.id()));
+                       if (route) {
+                               boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
+                               // pan for mono input routes, or stereo linked panners
+                               if (panner) {
+                                       double p = panner->position ();
+                                        
+                                       // calculate new value, and adjust
+                                       p += state.delta * state.sign;
+                                       p = min (1.0, p);
+                                       p = max (0.0, p);
+                                       panner->set_position (p);
+                               }
+                       } else {
+                               // it's a pot for an umnapped route, so turn all the lights off
+                               _port->write (builder.build_led_ring (dynamic_cast<Pot &> (control), off));
+                       }
+               } else {
+                       if (control.is_jog()) {
+                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", state.ticks));
+                               if (_jog_wheel) {
+                                       _jog_wheel->jog_event (*_port, control, state);
+                               }
+                       } else {
+                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("External controller moved %1\n", state.ticks));
+                               cout << "external controller" << state.ticks * state.sign << endl;
+                       }
+               }
+               break;
+               
+       default:
+               break;
+       }
+}
+
+void 
+Surface::handle_port_inactive (SurfacePort * port)
+{
+       _active = false;
+}
+
+void 
+Surface::write_sysex (const MidiByteArray & mba)
+{
+       if (mba.empty()) {
+               return;
+       }
+
+       MidiByteArray buf;
+       buf << sysex_hdr() << mba << MIDI::eox;
+       _port->write (buf);
+}
+
+void 
+Surface::write_sysex (MIDI::byte msg)
+{
+       MidiByteArray buf;
+       buf << sysex_hdr() << msg << MIDI::eox;
+       _port->write (buf);
+}
+
+void
+Surface::drop_routes ()
+{
+       for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+               (*s)->set_route (boost::shared_ptr<Route>());
+       }
+}
+
+uint32_t
+Surface::n_strips () const
+{
+       return strips.size();
+}
+
+Strip*
+Surface::nth_strip (uint32_t n) const
+{
+       if (n > n_strips()) {
+               return 0;
+       }
+       return strips[n];
+}
+
+void
+Surface::zero_all ()
+{
+       // TODO turn off Timecode displays
+
+       // zero all strips
+       for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
+               _port->write (builder.zero_strip (*this, **it));
+       }
+
+       // turn off global buttons and leds
+        // global buttons are only ever on mcu_port, so we don't have
+       // to figure out which port.
+
+       for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
+               Control & control = **it;
+               if (!control.group().is_strip() && control.accepts_feedback()) {
+                       _port->write (builder.zero_control (control));
+               }
+       }
+
+       // any hardware-specific stuff
+       // clear 2-char display
+       _port->write (builder.two_char_display ("LC"));
+
+       // and the led ring for the master strip
+       blank_jog_ring ();
+}
+
+void
+Surface::periodic ()
+{
+       for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+               (*s)->periodic ();
+       }
+}
+
+void
+Surface::write (const MidiByteArray& data) 
+{
+       _port->write (data);
+}
+
+void 
+Surface::jog_wheel_state_display (JogWheel::State state)
+{
+       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;
+       }
+}
+
index ae8b5ad142db60a6aabc29c86e7690469d18f362..9193260f5f99c09ebe44bb3c149a093efa3a8877 100644 (file)
@@ -1,9 +1,21 @@
 #ifndef mackie_surface_h
 #define mackie_surface_h
 
+#include <stdint.h>
+
+#include "midi++/types.h"
+
 #include "controls.h"
 #include "types.h"
-#include <stdint.h>
+#include "mackie_midi_builder.h"
+#include "mackie_jog_wheel.h"
+
+namespace MIDI {
+       class Parser;
+}
+
+class MidiByteArray;
+class MackieControlProtocol;
 
 namespace Mackie
 {
@@ -19,53 +31,23 @@ class Pot;
 class Led;
 class LedRing;
 
-/**
-       This represents an entire control surface, made up of Groups,
-       Strips and Controls. There are several collections for
-       ease of addressing in different ways, but only one collection
-       has definitive ownership.
-
-       It handles mapping button ids to press_ and release_ calls.
-
-       There are various emulations of the Mackie around, so specific
-       emulations will inherit from this to change button mapping, or 
-       have 7 fader channels instead of 8, or whatever.
-
-       Currently there are BcfSurface and MackieSurface.
-
-       TODO maybe make Group inherit from Control, for ease of ownership.
-*/
-class Surface
+class Surface : public PBD::ScopedConnectionList
 {
 public:
-       /**
-               A Surface can be made up of multiple units. eg one Mackie MCU plus
-               one or more Mackie MCU extenders.
-               
-               \param max_strips is the number of strips for the entire surface.
-               \param unit_strips is the number of strips per unit.
-       */
-
-       Surface (uint32_t max_strips, uint32_t unit_strips);
+       Surface (MackieControlProtocol&, jack_client_t* jack, const std::string& device_name, uint32_t number, surface_type_t stype);
        virtual ~Surface();
 
-       /// Calls the virtual initialisation methods. This *must* be called after
-       /// construction, because c++ is too dumb to call virtual methods from
-       /// inside a constructor
-       void init();
+       surface_type_t type() const { return _stype; }
+       uint32_t number() const { return _number; }
+
+       MackieControlProtocol& mcp() const { return _mcp; }
+
+       bool active() const { return _active; }
+       void drop_routes ();
 
        typedef std::vector<Control*> Controls;
-       
-       /// This collection has ownership of all the controls
        Controls controls;
 
-       /**
-               These are alternative addressing schemes
-               They use maps because the indices aren't always
-               0-based.
-               
-               Indexed by raw_id not by id. @see Control for the distinction.
-       */
        std::map<int,Fader*> faders;
        std::map<int,Pot*> pots;
        std::map<int,Button*> buttons;
@@ -75,39 +57,63 @@ public:
        /// no strip controls in here because they usually
        /// have the same names.
        std::map<std::string,Control*> controls_by_name;
+       
+       Mackie::JogWheel* jog_wheel() const { return _jog_wheel; }
 
        /// The collection of all numbered strips. No master
        /// strip in here.
        typedef std::vector<Strip*> Strips;
        Strips strips;
 
+       uint32_t n_strips () const;
+       Strip* nth_strip (uint32_t n) const;
+
        /// This collection owns the groups
        typedef std::map<std::string,Group*> Groups;
        Groups groups;
 
-       uint32_t max_strips() const { return _max_strips; }
-       
-public:
+       SurfacePort& port() const { return *_port; }
+
+       const MidiByteArray& sysex_hdr() const;
+
+       void periodic ();
+
+       void handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t, uint32_t channel_id);
+       void handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes*);
+       void handle_midi_note_on_message (MIDI::Parser&, MIDI::EventTwoBytes*);
+
+       /// Connect the any signal from the parser to handle_midi_any
+       /// unless it's already connected
+       void connect_to_signals ();
+
+       /// notification from a MackiePort that it's now inactive
+       void handle_port_inactive(Mackie::SurfacePort *);
+
+       /// write a sysex message
+       void write_sysex (const MidiByteArray& mba);
+       void write_sysex (MIDI::byte msg);
+       /// proxy write for port
+       void write (const MidiByteArray&);
+
        /// display an indicator of the first switched-in Route. Do nothing by default.
-       virtual void display_bank_start( SurfacePort &, MackieMidiBuilder &, uint32_t /*current_bank*/ ) {};
+       void display_bank_start (uint32_t /*current_bank*/);
                
-       /// called from MackieControlPRotocol::zero_all to turn things off
-       virtual void zero_all( SurfacePort &, MackieMidiBuilder & ) {};
+       /// called from MackieControlProtocol::zero_all to turn things off
+       void zero_all ();
 
        /// 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 & ) {};
+       void blank_jog_ring ();
+
+       bool has_timecode_display() const;
+       void display_timecode (const std::string & /*timecode*/, const std::string & /*timecode_last*/);
 
-       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;
+       float scrub_scaling_factor() const;
 
        /**
                The scaling factor function for speed increase and decrease. At
@@ -116,14 +122,25 @@ public:
                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;
+       float scaled_delta (const ControlState & state, float current_speed);
+
+       void handle_control_event (Mackie::Control & control, const Mackie::ControlState & state);
+
+  protected:
+       void init_controls();
+       void init_strips ();
 
-protected:
-       virtual void init_controls();
-       virtual void init_strips ();
+  private:
+       MackieControlProtocol& _mcp;
+       SurfacePort* _port;
+       surface_type_t _stype;
+       uint32_t _number;
+       bool _active;
+       bool _connected;
+       Mackie::JogWheel* _jog_wheel;
+       MackieMidiBuilder builder;
 
-       const uint32_t _max_strips;
-       const uint32_t _unit_strips;
+       void jog_wheel_state_display (Mackie::JogWheel::State state);
 };
 
 }
index 5a336447c812c18dd6c84ce3724ac1135a4cfa27..5fcea4c98fe9621157ef25faea6803d71dfcff66 100644 (file)
        along with this program; if not, write to the Free Software
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
-#include "surface_port.h"
 
-#include "mackie_control_exception.h"
-#include "controls.h"
+#include <sstream>
+#include <cstring>
+#include <cerrno>
+
+#include <sigc++/sigc++.h>
+#include <boost/shared_array.hpp>
 
 #include "midi++/types.h"
 #include "midi++/port.h"
 #include "midi++/manager.h"
-#include <sigc++/sigc++.h>
-#include <boost/shared_array.hpp>
 
-#include "i18n.h"
+#include "ardour/debug.h"
+#include "ardour/rc_configuration.h"
 
-#include <sstream>
+#include "controls.h"
+#include "mackie_control_exception.h"
+#include "surface.h"
+#include "surface_port.h"
 
-#include <cstring>
-#include <cerrno>
+
+#include "i18n.h"
 
 using namespace std;
 using namespace Mackie;
-
-SurfacePort::SurfacePort()
-       : _input_port (0), _output_port (0), _number (0), _active (false)
-{
-}
+using namespace PBD;
 
 /** @param input_port Input MIDI::Port; this object takes responsibility for removing it from
  *  the MIDI::Manager and destroying it.
  *  @param output_port Output MIDI::Port; responsibility similarly taken.
  */
-SurfacePort::SurfacePort (MIDI::Port & input_port, MIDI::Port & output_port, int number)
-       : _input_port (&input_port), _output_port (&output_port), _number (number), _active (false)
+SurfacePort::SurfacePort (Surface& s, MIDI::Port & input_port, MIDI::Port & output_port)
+       : _surface (&s)
+       , _input_port (&input_port)
+       , _output_port (&output_port)
+       , _active (false)
 {
 }
 
 SurfacePort::~SurfacePort()
 {
-#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;
+       close ();
 
        MIDI::Manager* mm = MIDI::Manager::instance ();
        
@@ -70,10 +69,6 @@ SurfacePort::~SurfacePort()
                mm->remove_port (_output_port);
                delete _output_port;
        }
-       
-#ifdef PORT_DEBUG
-       cout << "~SurfacePort::SurfacePort() finished" << endl;
-#endif
 }
 
 // wrapper for one day when strerror_r is working properly
@@ -96,18 +91,7 @@ MidiByteArray SurfacePort::read()
        }
        
        // 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;
-               return retval;
-       }
-       
-       // check active again - destructor sequence
-       if  (!active()) return retval;
-#endif
-       
+
        // read port and copy to return value
        int nread = input_port().read (buf, sizeof (buf));
 
@@ -150,8 +134,6 @@ void SurfacePort::write (const MidiByteArray & mba)
        // that the destructor doesn't destroy the mutex while
        // it's still in use
        if (!active()) return;
-       Glib::RecMutex::Lock lock (_rwlock);
-       if (!active()) return;
 
        int count = output_port().write (mba.bytes().get(), mba.size(), 0);
        if  (count != (int)mba.size()) {
@@ -171,24 +153,114 @@ void SurfacePort::write (const MidiByteArray & mba)
 #endif
 }
 
-void SurfacePort::write_sysex (const MidiByteArray & mba)
+
+void SurfacePort::open()
 {
-       if (mba.empty()) {
-               return;
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::open %1\n", *this));
+       input_port().parser()->sysex.connect_same_thread (sysex_connection, boost::bind (&SurfacePort::handle_midi_sysex, this, _1, _2, _3));
+       _active = true;
+}
+
+void SurfacePort::close()
+{
+       DEBUG_TRACE (DEBUG::MackieControl, "SurfacePort::close\n");
+       sysex_connection.disconnect();
+
+       if (_surface) {
+               // faders to minimum
+               _surface->write_sysex (0x61);
+               // All LEDs off
+               _surface->write_sysex (0x62);
+               // Reset (reboot into offline mode)
+               _surface->write_sysex (0x63);
+       }
+
+       _active = false;
+}
+
+void 
+SurfacePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
+{
+       MidiByteArray bytes (count, raw_bytes);
+
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
+
+       switch (bytes[5])
+       {
+               case 0x01:
+                       _surface->write_sysex (host_connection_query (bytes));
+                       break;
+               case 0x03:
+                       // not used right now
+                       _surface->write_sysex (host_connection_confirmation (bytes));
+                       break;
+               case 0x04:
+                       inactive_event ();
+                       cout << "host connection error" << bytes << endl;
+                       break;
+               case 0x14:
+                       // probe_emulation (bytes);
+                       break;
+               default:
+                       cout << "unknown sysex: " << bytes << endl;
+       }
+}
+
+MidiByteArray calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
+{
+       MidiByteArray l;
+       back_insert_iterator<MidiByteArray> back  (l);
+       copy (begin, end, back);
+       
+       MidiByteArray retval;
+       
+       // this is how to calculate the response to the challenge.
+       // from the Logic docs.
+       retval <<  (0x7f &  (l[0] +  (l[1] ^ 0xa) - l[3]));
+       retval <<  (0x7f &  ( (l[2] >> l[3]) ^  (l[0] + l[3])));
+       retval <<  (0x7f &  ((l[3] -  (l[2] << 2)) ^  (l[0] | l[1])));
+       retval <<  (0x7f &  (l[1] - l[2] +  (0xf0 ^  (l[3] << 4))));
+       
+       return retval;
+}
+
+// not used right now
+MidiByteArray SurfacePort::host_connection_query (MidiByteArray & bytes)
+{
+       MidiByteArray response;
+
+       // handle host connection query
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
+       
+       if  (bytes.size() != 18) {
+               cerr << "expecting 18 bytes, read " << bytes << " from " << input_port().name() << endl;
+               return response;
        }
 
-       MidiByteArray buf;
-       buf << sysex_hdr() << mba << MIDI::eox;
-       write (buf);
+       // build and send host connection reply
+       response << 0x02;
+       copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
+       response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
+       return response;
 }
 
-void SurfacePort::write_sysex (MIDI::byte msg)
+// not used right now
+MidiByteArray SurfacePort::host_connection_confirmation (const MidiByteArray & bytes)
 {
-       MidiByteArray buf;
-       buf << sysex_hdr() << msg << MIDI::eox;
-       write (buf);
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
+       
+       // decode host connection confirmation
+       if  (bytes.size() != 14) {
+               ostringstream os;
+               os << "expecting 14 bytes, read " << bytes << " from " << input_port().name();
+               throw MackieControlException (os.str());
+       }
+       
+       // send version request
+       return MidiByteArray (2, 0x13, 0x00);
 }
 
+
 ostream & Mackie::operator <<  (ostream & os, const SurfacePort & port)
 {
        os << "{ ";
index 63966c6f3af60d5b29212786b51d4418737a610f..8b66073b4af71dedb988cfef5004b6cb7fe2561b 100644 (file)
@@ -18,6 +18,7 @@
 #ifndef surface_port_h
 #define surface_port_h
 
+#include <midi++/types.h>
 #include <glibmm/thread.h>
 
 #include "pbd/signals.h"
 
 namespace MIDI {
        class Port;
+       class Parser;
 }
 
+class MackieControlProtocol;
+
 namespace Mackie
 {
 
+class Surface;
+
 /**
        Make a relationship between a midi port and a Mackie device.
 */
-class SurfacePort : public PBD::ScopedConnectionList
+
+class SurfacePort 
 {
 public:
-       SurfacePort (MIDI::Port & input_port, MIDI::Port & output_port, int number);
+       SurfacePort (Mackie::Surface&, MIDI::Port& input_port, MIDI::Port& output_port);
        virtual ~SurfacePort();
        
-       // when this is successful, active() should return true
-       virtual void open() = 0;
-       
-       // subclasses should call this before doing their own close
-       virtual void close() = 0;
+       void open();
+       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();
+       MidiByteArray read();
        
        /// an easier way to output bytes via midi
-       virtual void write( const MidiByteArray & );
+       void write (const MidiByteArray&);
        
-       /// write a sysex message
-       void write_sysex( const MidiByteArray & mba );
-       void write_sysex( MIDI::byte msg );
-
-       /// return the correct sysex header for this port
-       virtual const MidiByteArray & sysex_hdr() const = 0;
-
-       MIDI::Port & input_port() { return *_input_port; }
-       const MIDI::Port & input_port() const { return *_input_port; }
-       MIDI::Port & output_port() { return *_output_port; }
-       const MIDI::Port & output_port() const { return *_output_port; }
-       
-       // emitted just before the port goes into initialisation
-       // where it tries to establish that its device is connected
-       PBD::Signal0<void> init_event;
-       
-       // emitted when the port completes initialisation successfully
-       PBD::Signal0<void> active_event;
+       MIDI::Port& input_port() { return *_input_port; }
+       const MIDI::Port& input_port() const { return *_input_port; }
+       MIDI::Port& output_port() { return *_output_port; }
+       const MIDI::Port& output_port() const { return *_output_port; }
 
        // emitted when the port goes inactive (ie a read or write failed)
        PBD::Signal0<void> inactive_event;
        
-       // the port number - master is 0(extenders are 1((,4
-       virtual int number() const { return _number; }
-       
-       // number of strips handled by this port. Usually 8.
-       virtual int strips() const = 0;
+       void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
 
-       virtual bool active() const { return _active; }
-       virtual void active( bool yn ) { _active = yn; }
+       bool active() const { return _active; }
 
-       void add_in_use_timeout (Control &, Control *);
-       
 protected:
-       /// Only for use by DummyPort
-       SurfacePort();
+       MidiByteArray host_connection_query (MidiByteArray& bytes);
+       MidiByteArray host_connection_confirmation (const MidiByteArray& bytes);
 
-       virtual void control_event (SurfacePort &, Control &, const ControlState &) {}
-       
 private:
-       MIDI::Port * _input_port;
-       MIDI::Port * _output_port;
-       int _number;
-       bool _active;
+       Mackie::Surface* _surface;
+       MIDI::Port*      _input_port;
+       MIDI::Port*      _output_port;
+       bool             _active;
 
-       Glib::RecMutex _rwlock;
+       PBD::ScopedConnection sysex_connection;
 };     
 
-std::ostream & operator << ( std::ostream & , const SurfacePort & port );
+std::ostream& operator <<  (std::ostream& , const SurfacePort& port);
 
 }
 
index be5c7e8b79e49295541069d0c8cae4ed11099e9f..4fc52f66e4b498d1b58c568965d94e9c84440673 100644 (file)
 namespace Mackie
 {
 
+enum surface_type_t { 
+       mcu, 
+       ext, 
+};
+
 /**
        This started off as an enum, but it got really annoying
        typing ? on : off
index 2d0479ef1832e70de86543db401142ab6da2a28c..9aac5ce811e1de55eb6e796c9069dbb9d156735a 100644 (file)
@@ -21,23 +21,17 @@ def configure(conf):
 def build(bld):
     obj = bld(features = 'cxx cxxshlib')
     obj.source = '''
-            bcf_surface.cc
             button.cc
             controls.cc
-            dummy_port.cc
             fader.cc
             gui.cc
             interface.cc
             mackie_control_protocol.cc
-            mackie_control_protocol_poll.cc
             mackie_jog_wheel.cc
             mackie_midi_builder.cc
-            mackie_port.cc
-            mackie_surface.cc
             mcp_buttons.cc
             meter.cc
             midi_byte_array.cc
-            route_signal.cc
             strip.cc
             surface.cc
             surface_port.cc