summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2012-04-10 14:27:44 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2012-04-10 14:27:44 +0000
commit5ace191bff634c0b09eca5e69065afcb7d0cb183 (patch)
tree26c7a337e87c2f3e419923016342b1b1b06dbcd6 /libs
parentac7ade93bda207d60a5276f8fea5a1f01567095b (diff)
drastic, fundamental redesign of MCP code
git-svn-id: svn://localhost/ardour2/branches/3.0@11861 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/surfaces/mackie/bcf_surface.cc46
-rw-r--r--libs/surfaces/mackie/bcf_surface.h31
-rw-r--r--libs/surfaces/mackie/button.cc4
-rw-r--r--libs/surfaces/mackie/button.h8
-rw-r--r--libs/surfaces/mackie/controls.cc17
-rw-r--r--libs/surfaces/mackie/controls.h12
-rw-r--r--libs/surfaces/mackie/dummy_port.cc58
-rw-r--r--libs/surfaces/mackie/dummy_port.h61
-rw-r--r--libs/surfaces/mackie/fader.cc6
-rw-r--r--libs/surfaces/mackie/fader.h8
-rw-r--r--libs/surfaces/mackie/jog.h6
-rw-r--r--libs/surfaces/mackie/led.h6
-rw-r--r--libs/surfaces/mackie/ledring.h4
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc963
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.h116
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol_poll.cc54
-rw-r--r--libs/surfaces/mackie/mackie_jog_wheel.cc70
-rw-r--r--libs/surfaces/mackie/mackie_midi_builder.cc28
-rw-r--r--libs/surfaces/mackie/mackie_midi_builder.h49
-rw-r--r--libs/surfaces/mackie/mackie_port.cc416
-rw-r--r--libs/surfaces/mackie/mackie_port.h125
-rw-r--r--libs/surfaces/mackie/mackie_surface.cc24
-rw-r--r--libs/surfaces/mackie/mackie_surface.h27
-rw-r--r--libs/surfaces/mackie/mcp_buttons.cc47
-rw-r--r--libs/surfaces/mackie/meter.cc4
-rw-r--r--libs/surfaces/mackie/meter.h6
-rw-r--r--libs/surfaces/mackie/pot.h8
-rw-r--r--libs/surfaces/mackie/route_signal.cc105
-rw-r--r--libs/surfaces/mackie/route_signal.h92
-rw-r--r--libs/surfaces/mackie/strip.cc312
-rw-r--r--libs/surfaces/mackie/strip.h62
-rw-r--r--libs/surfaces/mackie/surface.cc452
-rw-r--r--libs/surfaces/mackie/surface.h133
-rw-r--r--libs/surfaces/mackie/surface_port.cc172
-rw-r--r--libs/surfaces/mackie/surface_port.h73
-rw-r--r--libs/surfaces/mackie/types.h5
-rw-r--r--libs/surfaces/mackie/wscript6
37 files changed, 1362 insertions, 2254 deletions
diff --git a/libs/surfaces/mackie/bcf_surface.cc b/libs/surfaces/mackie/bcf_surface.cc
deleted file mode 100644
index 756e2139c5..0000000000
--- a/libs/surfaces/mackie/bcf_surface.cc
+++ /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
index 47ce8a1216..0000000000
--- a/libs/surfaces/mackie/bcf_surface.h
+++ /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
diff --git a/libs/surfaces/mackie/button.cc b/libs/surfaces/mackie/button.cc
index 5312f1ae86..714294ed7d 100644
--- a/libs/surfaces/mackie/button.cc
+++ b/libs/surfaces/mackie/button.cc
@@ -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);
diff --git a/libs/surfaces/mackie/button.h b/libs/surfaces/mackie/button.h
index 97cd1e309f..2a4dfe8496 100644
--- a/libs/surfaces/mackie/button.h
+++ b/libs/surfaces/mackie/button.h
@@ -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;
diff --git a/libs/surfaces/mackie/controls.cc b/libs/surfaces/mackie/controls.cc
index 7e0a98a007..745da21969 100644
--- a/libs/surfaces/mackie/controls.cc
+++ b/libs/surfaces/mackie/controls.cc
@@ -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);
diff --git a/libs/surfaces/mackie/controls.h b/libs/surfaces/mackie/controls.h
index 81923f23c3..ca972ed5e0 100644
--- a/libs/surfaces/mackie/controls.h
+++ b/libs/surfaces/mackie/controls.h
@@ -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
index 7654f8f987..0000000000
--- a/libs/surfaces/mackie/dummy_port.cc
+++ /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
index 056e30fa0d..0000000000
--- a/libs/surfaces/mackie/dummy_port.h
+++ /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
diff --git a/libs/surfaces/mackie/fader.cc b/libs/surfaces/mackie/fader.cc
index a7283f42d1..80928d85eb 100644
--- a/libs/surfaces/mackie/fader.cc
+++ b/libs/surfaces/mackie/fader.cc
@@ -24,11 +24,9 @@
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);
diff --git a/libs/surfaces/mackie/fader.h b/libs/surfaces/mackie/fader.h
index b8c72b8753..7c64016826 100644
--- a/libs/surfaces/mackie/fader.h
+++ b/libs/surfaces/mackie/fader.h
@@ -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&);
};
}
diff --git a/libs/surfaces/mackie/jog.h b/libs/surfaces/mackie/jog.h
index ec3517348b..c769319766 100644
--- a/libs/surfaces/mackie/jog.h
+++ b/libs/surfaces/mackie/jog.h
@@ -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&);
};
}
diff --git a/libs/surfaces/mackie/led.h b/libs/surfaces/mackie/led.h
index 3577e6f0e4..b2bc29ec5f 100644
--- a/libs/surfaces/mackie/led.h
+++ b/libs/surfaces/mackie/led.h
@@ -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&);
};
}
diff --git a/libs/surfaces/mackie/ledring.h b/libs/surfaces/mackie/ledring.h
index 3ab95f211a..284c2d26a3 100644
--- a/libs/surfaces/mackie/ledring.h
+++ b/libs/surfaces/mackie/ledring.h
@@ -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)
{
}
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index 96de6b222d..40f62ba680 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -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"
@@ -57,12 +56,9 @@
#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();;
+ }
+ }
}
+
diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h
index b7f3a2b30d..0232cd46d7 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.h
+++ b/libs/surfaces/mackie/mackie_control_protocol.h
@@ -23,22 +23,22 @@
#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
diff --git a/libs/surfaces/mackie/mackie_control_protocol_poll.cc b/libs/surfaces/mackie/mackie_control_protocol_poll.cc
index 06aafc5965..66c80c9a8b 100644
--- a/libs/surfaces/mackie/mackie_control_protocol_poll.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol_poll.cc
@@ -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
-}
diff --git a/libs/surfaces/mackie/mackie_jog_wheel.cc b/libs/surfaces/mackie/mackie_jog_wheel.cc
index 95ab97c5f7..8439c68fe7 100644
--- a/libs/surfaces/mackie/mackie_jog_wheel.cc
+++ b/libs/surfaces/mackie/mackie_jog_wheel.cc
@@ -14,17 +14,17 @@
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);
}
diff --git a/libs/surfaces/mackie/mackie_midi_builder.cc b/libs/surfaces/mackie/mackie_midi_builder.cc
index 714180112d..a57725baa5 100644
--- a/libs/surfaces/mackie/mackie_midi_builder.cc
+++ b/libs/surfaces/mackie/mackie_midi_builder.cc
@@ -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;
diff --git a/libs/surfaces/mackie/mackie_midi_builder.h b/libs/surfaces/mackie/mackie_midi_builder.h
index 1b33c75913..e4f929a588 100644
--- a/libs/surfaces/mackie/mackie_midi_builder.h
+++ b/libs/surfaces/mackie/mackie_midi_builder.h
@@ -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
index 8c967b787d..0000000000
--- a/libs/surfaces/mackie/mackie_port.cc
+++ /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
index 1b6b77da93..0000000000
--- a/libs/surfaces/mackie/mackie_port.h
+++ /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
index 415d4b1c4e..0000000000
--- a/libs/surfaces/mackie/mackie_surface.cc
+++ /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
index 18fcab5620..0000000000
--- a/libs/surfaces/mackie/mackie_surface.h
+++ /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
diff --git a/libs/surfaces/mackie/mcp_buttons.cc b/libs/surfaces/mackie/mcp_buttons.cc
index 84674cb7af..253b39db82 100644
--- a/libs/surfaces/mackie/mcp_buttons.cc
+++ b/libs/surfaces/mackie/mcp_buttons.cc
@@ -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 {
diff --git a/libs/surfaces/mackie/meter.cc b/libs/surfaces/mackie/meter.cc
index a7dbf25831..5bc7c49096 100644
--- a/libs/surfaces/mackie/meter.cc
+++ b/libs/surfaces/mackie/meter.cc
@@ -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);
diff --git a/libs/surfaces/mackie/meter.h b/libs/surfaces/mackie/meter.h
index 70d44e4515..7110f04416 100644
--- a/libs/surfaces/mackie/meter.h
+++ b/libs/surfaces/mackie/meter.h
@@ -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;
diff --git a/libs/surfaces/mackie/pot.h b/libs/surfaces/mackie/pot.h
index 42e5c643e8..767ea6e6df 100644
--- a/libs/surfaces/mackie/pot.h
+++ b/libs/surfaces/mackie/pot.h
@@ -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
index f40fff48ba..0000000000
--- a/libs/surfaces/mackie/route_signal.cc
+++ /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
index 59bfc66e7b..0000000000
--- a/libs/surfaces/mackie/route_signal.h
+++ /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
diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc
index e20f33e1df..fd9f4299d0 100644
--- a/libs/surfaces/mackie/strip.cc
+++ b/libs/surfaces/mackie/strip.cc
@@ -21,6 +21,22 @@
#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"
@@ -31,23 +47,16 @@
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));
+}
diff --git a/libs/surfaces/mackie/strip.h b/libs/surfaces/mackie/strip.h
index 74928181c7..97997e7f0b 100644
--- a/libs/surfaces/mackie/strip.h
+++ b/libs/surfaces/mackie/strip.h
@@ -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__ */
diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc
index 9ec426179d..dda453087d 100644
--- a/libs/surfaces/mackie/surface.cc
+++ b/libs/surfaces/mackie/surface.cc
@@ -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"
@@ -19,29 +30,73 @@
#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;
+ }
+}
+
diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h
index ae8b5ad142..9193260f5f 100644
--- a/libs/surfaces/mackie/surface.h
+++ b/libs/surfaces/mackie/surface.h
@@ -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);
};
}
diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc
index 5a336447c8..5fcea4c98f 100644
--- a/libs/surfaces/mackie/surface_port.cc
+++ b/libs/surfaces/mackie/surface_port.cc
@@ -15,49 +15,48 @@
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 << "{ ";
diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h
index 63966c6f3a..8b66073b4a 100644
--- a/libs/surfaces/mackie/surface_port.h
+++ b/libs/surfaces/mackie/surface_port.h
@@ -18,6 +18,7 @@
#ifndef surface_port_h
#define surface_port_h
+#include <midi++/types.h>
#include <glibmm/thread.h>
#include "pbd/signals.h"
@@ -26,82 +27,62 @@
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);
}
diff --git a/libs/surfaces/mackie/types.h b/libs/surfaces/mackie/types.h
index be5c7e8b79..4fc52f66e4 100644
--- a/libs/surfaces/mackie/types.h
+++ b/libs/surfaces/mackie/types.h
@@ -23,6 +23,11 @@
namespace Mackie
{
+enum surface_type_t {
+ mcu,
+ ext,
+};
+
/**
This started off as an enum, but it got really annoying
typing ? on : off
diff --git a/libs/surfaces/mackie/wscript b/libs/surfaces/mackie/wscript
index 2d0479ef18..9aac5ce811 100644
--- a/libs/surfaces/mackie/wscript
+++ b/libs/surfaces/mackie/wscript
@@ -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