start breaking apart the various controls into their own headers and source code, and making each control know how to generate MIDI; throttle delivery of meter data and get meter ID right

git-svn-id: svn://localhost/ardour2/branches/3.0@11846 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2012-04-09 13:59:35 +00:00
parent 63e15e1737
commit f02edae438
24 changed files with 893 additions and 551 deletions

View file

@ -4,10 +4,13 @@
#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)
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

View file

@ -0,0 +1,49 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_mackie_control_protocol_button_h__
#define __ardour_mackie_control_protocol_button_h__
#include "controls.h"
#include "led.h"
namespace Mackie {
class Surface;
class Button : public Control
{
public:
Button (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
, _led (id, ordinal, name + "_led", group) {}
virtual const Led & led() const { return _led; }
virtual type_t type() const { return type_button; };
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
private:
Led _led;
};
}
#endif

View file

@ -0,0 +1,42 @@
#ifndef __ardour_mackie_control_protocol_control_group_h__
#define __ardour_mackie_control_protocol_control_group_h__
#include <vector>
namespace Mackie {
class Control;
/**
This is a loose group of controls, eg cursor buttons,
transport buttons, functions buttons etc.
*/
class Group
{
public:
Group (const std::string & name)
: _name (name) {}
virtual ~Group() {}
virtual bool is_strip() const { return false; }
virtual bool is_master() const { return false; }
virtual void add (Control & control);
const std::string & name() const { return _name; }
void set_name (const std::string & rhs) { _name = rhs; }
typedef std::vector<Control*> Controls;
const Controls & controls() const { return _controls; }
protected:
Controls _controls;
private:
std::string _name;
};
}
#endif

View file

@ -24,6 +24,16 @@
#include "types.h"
#include "mackie_midi_builder.h"
#include "surface.h"
#include "control_group.h"
#include "button.h"
#include "led.h"
#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
using namespace Mackie;
using namespace std;
@ -33,77 +43,6 @@ void Group::add (Control& control)
_controls.push_back (&control);
}
Strip::Strip (const std::string& name, int index)
: Group (name)
, _solo (0)
, _recenable (0)
, _mute (0)
, _select (0)
, _vselect (0)
, _fader_touch (0)
, _vpot (0)
, _gain (0)
, _index (index)
{
/* master strip only */
}
Strip::Strip (Surface& surface, const std::string& name, int index, int unit_index, StripControlDefinition* ctls)
: Group (name)
, _solo (0)
, _recenable (0)
, _mute (0)
, _select (0)
, _vselect (0)
, _fader_touch (0)
, _vpot (0)
, _gain (0)
, _index (index)
{
/* 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 + unit_index, unit_index+1, ctls[i].name, *this);
}
}
/**
TODO could optimise this to use enum, but it's only
called during the protocol class instantiation.
*/
void Strip::add (Control & control)
{
Group::add (control);
if (control.name() == "gain") {
_gain = reinterpret_cast<Fader*>(&control);
} else if (control.name() == "vpot") {
_vpot = reinterpret_cast<Pot*>(&control);
} else if (control.name() == "recenable") {
_recenable = reinterpret_cast<Button*>(&control);
} else if (control.name() == "solo") {
_solo = reinterpret_cast<Button*>(&control);
} else if (control.name() == "mute") {
_mute = reinterpret_cast<Button*>(&control);
} else if (control.name() == "select") {
_select = reinterpret_cast<Button*>(&control);
} else if (control.name() == "vselect") {
_vselect = reinterpret_cast<Button*>(&control);
} else if (control.name() == "fader_touch") {
_fader_touch = reinterpret_cast<Button*>(&control);
} else if (control.name() == "meter") {
_meter = reinterpret_cast<Meter*>(&control);
} else if (control.type() == Control::type_led || control.type() == Control::type_led_ring) {
// relax
} else {
ostringstream os;
os << "Strip::add: unknown control type " << control;
throw MackieControlException (os.str());
}
}
Control::Control (int id, int ordinal, std::string name, Group & group)
: _id (id)
, _ordinal (ordinal)
@ -113,86 +52,6 @@ Control::Control (int id, int ordinal, std::string name, Group & group)
{
}
Fader&
Strip::gain()
{
if (_gain == 0) {
throw MackieControlException ("gain is null");
}
return *_gain;
}
Pot&
Strip::vpot()
{
if (_vpot == 0) {
throw MackieControlException ("vpot is null");
}
return *_vpot;
}
Button&
Strip::recenable()
{
if (_recenable == 0) {
throw MackieControlException ("recenable is null");
}
return *_recenable;
}
Button&
Strip::solo()
{
if (_solo == 0) {
throw MackieControlException ("solo is null");
}
return *_solo;
}
Button&
Strip::mute()
{
if (_mute == 0) {
throw MackieControlException ("mute is null");
}
return *_mute;
}
Button&
Strip::select()
{
if (_select == 0) {
throw MackieControlException ("select is null");
}
return *_select;
}
Button&
Strip::vselect()
{
if (_vselect == 0) {
throw MackieControlException ("vselect is null");
}
return *_vselect;
}
Button&
Strip::fader_touch()
{
if (_fader_touch == 0) {
throw MackieControlException ("fader_touch is null");
}
return *_fader_touch;
}
Meter&
Strip::meter()
{
if (_meter == 0) {
throw MackieControlException ("meter is null");
}
return *_meter;
}
/** @return true if the control is in use, or false otherwise.
Buttons are `in use' when they are held down.
Faders with touch support are `in use' when they are being touched.
@ -231,30 +90,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control)
return os;
}
std::ostream & Mackie::operator << (std::ostream & os, const Strip & strip)
{
os << typeid (strip).name();
os << " { ";
os << "has_solo: " << boolalpha << strip.has_solo();
os << ", ";
os << "has_recenable: " << boolalpha << strip.has_recenable();
os << ", ";
os << "has_mute: " << boolalpha << strip.has_mute();
os << ", ";
os << "has_select: " << boolalpha << strip.has_select();
os << ", ";
os << "has_vselect: " << boolalpha << strip.has_vselect();
os << ", ";
os << "has_fader_touch: " << boolalpha << strip.has_fader_touch();
os << ", ";
os << "has_vpot: " << boolalpha << strip.has_vpot();
os << ", ";
os << "has_gain: " << boolalpha << strip.has_gain();
os << " }";
return os;
}
Control*
Button::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
{
@ -305,12 +140,3 @@ Jog::factory (Surface& surface, int id, int ordinal, const char* name, Group& gr
return j;
}
Control*
Meter::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
{
Meter* m = new Meter (id, ordinal, name, group);
surface.meters[id] = m;
surface.controls.push_back (m);
group.add (*m);
return m;
}

View file

@ -32,119 +32,11 @@
namespace Mackie
{
class Control;
class Strip;
class Group;
class Led;
class Surface;
/**
This is a loose group of controls, eg cursor buttons,
transport buttons, functions buttons etc.
*/
class Group
{
public:
Group (const std::string & name)
: _name (name) {}
virtual ~Group() {}
virtual bool is_strip() const { return false; }
virtual bool is_master() const { return false; }
virtual void add (Control & control);
const std::string & name() const { return _name; }
void set_name (const std::string & rhs) { _name = rhs; }
typedef std::vector<Control*> Controls;
const Controls & controls() const { return _controls; }
protected:
Controls _controls;
private:
std::string _name;
};
class Button;
class Pot;
class Fader;
class Meter;
struct StripControlDefinition {
const char* name;
uint32_t base_id;
Control* (*factory)(Surface&, int index, int ordinal, const char* name, Group&);
};
struct GlobalControlDefinition {
const char* name;
uint32_t id;
Control* (*factory)(Surface&, int index, int ordinal, const char* name, Group&);
const char* group_name;
};
/**
This is the set of controls that make up a strip.
*/
class Strip : public Group
{
public:
Strip (const std::string& name, int index); /* master strip only */
Strip (Surface&, const std::string & name, int index, int unit_index, StripControlDefinition* ctls);
virtual bool is_strip() const { return true; }
virtual void add (Control & control);
int index() const { return _index; } // zero based
Button & solo();
Button & recenable();
Button & mute();
Button & select();
Button & vselect();
Button & fader_touch();
Pot & vpot();
Fader & gain();
Meter& meter ();
bool has_solo() const { return _solo != 0; }
bool has_recenable() const { return _recenable != 0; }
bool has_mute() const { return _mute != 0; }
bool has_select() const { return _select != 0; }
bool has_vselect() const { return _vselect != 0; }
bool has_fader_touch() const { return _fader_touch != 0; }
bool has_vpot() const { return _vpot != 0; }
bool has_gain() const { return _gain != 0; }
bool has_meter() const { return _meter != 0; }
private:
Button* _solo;
Button* _recenable;
Button* _mute;
Button* _select;
Button* _vselect;
Button* _fader_touch;
Pot* _vpot;
Fader* _gain;
Meter* _meter;
int _index;
};
std::ostream & operator << (std::ostream &, const Strip &);
class MasterStrip : public Strip
{
public:
MasterStrip (const std::string & name, int index)
: Strip (name, index) {}
virtual bool is_master() const { return true; }
};
class Led;
/**
The base class for controls on the surface. They deliberately
don't know the midi protocol for updating them.
*/
class Control
{
public:
@ -189,8 +81,6 @@ public:
const std::string & name() const { return _name; }
const Group & group() const { return _group; }
const Strip & strip() const { return dynamic_cast<const Strip&> (_group); }
Strip & strip() { return dynamic_cast<Strip&> (_group); }
virtual bool accepts_feedback() const { return true; }
virtual type_t type() const = 0;
@ -219,103 +109,6 @@ private:
std::ostream & operator << (std::ostream & os, const Control & control);
class Fader : public Control
{
public:
Fader (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
{
}
virtual type_t type() const { return type_fader; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
class Led : public Control
{
public:
Led (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
{
}
virtual const Led & led() const { return *this; }
virtual type_t type() const { return type_led; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
class Button : public Control
{
public:
Button (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
, _led (id, ordinal, name + "_led", group) {}
virtual const Led & led() const { return _led; }
virtual type_t type() const { return type_button; };
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
private:
Led _led;
};
class LedRing : public Led
{
public:
LedRing (int id, int ordinal, std::string name, Group & group)
: Led (id, ordinal, name, group)
{
}
virtual type_t type() const { return type_led_ring; }
};
class Pot : public Control
{
public:
Pot (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
, _led_ring (id, ordinal, name + "_ring", group) {}
virtual type_t type() const { return type_pot; }
virtual const LedRing & led_ring() const {return _led_ring; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
private:
LedRing _led_ring;
};
class Jog : public Pot
{
public:
Jog (int id, int ordinal, std::string name, Group & group)
: Pot (id, ordinal, name, group)
{
}
virtual bool is_jog() const { return true; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
class Meter : public Control
{
public:
Meter (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group) {}
virtual type_t type() const { return type_meter; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
}
#endif /* __mackie_controls_h__ */

View file

@ -0,0 +1,23 @@
#ifndef __ardour_mackie_control_protocol_fader_h__
#define __ardour_mackie_control_protocol_fader_h__
#include "controls.h"
namespace Mackie {
class Fader : public Control
{
public:
Fader (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
{
}
virtual type_t type() const { return type_fader; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
}
#endif

View file

@ -0,0 +1,43 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_mackie_control_protocol_jog_h__
#define __ardour_mackie_control_protocol_jog_h__
#include "controls.h"
#include "pot.h"
namespace Mackie {
class Jog : public Pot
{
public:
Jog (int id, int ordinal, std::string name, Group & group)
: Pot (id, ordinal, name, group)
{
}
virtual bool is_jog() const { return true; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
}
#endif /* __ardour_mackie_control_protocol_jog_h__ */

View file

@ -0,0 +1,44 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_mackie_control_protocol_led_h__
#define __ardour_mackie_control_protocol_led_h__
#include "controls.h"
namespace Mackie {
class Led : public Control
{
public:
Led (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
{
}
virtual const Led & led() const { return *this; }
virtual type_t type() const { return type_led; }
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
};
}
#endif /* __ardour_mackie_control_protocol_led_h__ */

View file

@ -0,0 +1,41 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_mackie_control_protocol_ledring_h__
#define __ardour_mackie_control_protocol_ledring_h__
#include "controls.h"
#include "led.h"
namespace Mackie {
class LedRing : public Led
{
public:
LedRing (int id, int ordinal, std::string name, Group & group)
: Led (id, ordinal, name, group)
{
}
virtual type_t type() const { return type_led_ring; }
};
}
#endif

View file

@ -3,6 +3,13 @@
*/
#include "mackie_button_handler.h"
#include "controls.h"
#include "button.h"
#include "led.h"
#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
#include <iostream>

View file

@ -64,6 +64,13 @@
#include "bcf_surface.h"
#include "mackie_surface.h"
#include "strip.h"
#include "control_group.h"
#include "meter.h"
#include "button.h"
#include "fader.h"
#include "pot.h"
using namespace ARDOUR;
using namespace std;
using namespace Mackie;
@ -106,6 +113,8 @@ MackieControlProtocol::~MackieControlProtocol()
{
DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::~MackieControlProtocol\n");
_active = false;
try {
close();
}
@ -400,11 +409,11 @@ MackieControlProtocol::set_active (bool yn)
// must come after _active = true otherwise it won't run
update_surface();
Glib::RefPtr<Glib::TimeoutSource> meter_timeout = Glib::TimeoutSource::create (100); // milliseconds
Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds
meter_connection = meter_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::meter_update));
periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &MackieControlProtocol::periodic));
meter_timeout->attach (main_loop()->get_context());
periodic_timeout->attach (main_loop()->get_context());
} else {
BaseUI::quit ();
@ -423,51 +432,29 @@ MackieControlProtocol::set_active (bool yn)
}
bool
MackieControlProtocol::meter_update ()
MackieControlProtocol::periodic ()
{
/* update automation while we're here */
poll_session_data ();
if (!_active) {
return false;
}
{
Glib::Mutex::Lock lm (route_signals_lock);
for (std::vector<RouteSignal*>::iterator r = route_signals.begin(); r != route_signals.end(); ++r) {
float dB;
dB = const_cast<PeakMeter&> ((*r)->route()->peak_meter()).peak_power (0);
Mackie::Meter& m = (*r)->strip().meter();
float def = 0.0f; /* Meter deflection %age */
if (dB < -70.0f) {
def = 0.0f;
} else if (dB < -60.0f) {
def = (dB + 70.0f) * 0.25f;
} else if (dB < -50.0f) {
def = (dB + 60.0f) * 0.5f + 2.5f;
} else if (dB < -40.0f) {
def = (dB + 50.0f) * 0.75f + 7.5f;
} else if (dB < -30.0f) {
def = (dB + 40.0f) * 1.5f + 15.0f;
} else if (dB < -20.0f) {
def = (dB + 30.0f) * 2.0f + 30.0f;
} else if (dB < 6.0f) {
def = (dB + 20.0f) * 2.5f + 50.0f;
} else {
def = 115.0f;
}
/* 115 is the deflection %age that would be
when dB=6.0. this is an arbitrary
endpoint for our scaling.
*/
(*r)->port().write (builder.build_meter (m, def/115.0));
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);
}
}
return _active; // call it again as long as we're active
update_timecode_display();
return true;
}
bool
@ -496,13 +483,17 @@ MackieControlProtocol::handle_strip_button (SurfacePort & port, Control & contro
if (control.name() == "fader_touch") {
state = bs == press;
control.strip().gain().set_in_use (state);
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, control.strip().gain(), &control.strip().fader_touch());
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());
}
}
}
@ -733,7 +724,7 @@ MackieControlProtocol::close()
port_connections.drop_connections ();
session_connections.drop_connections ();
route_connections.drop_connections ();
meter_connection.disconnect ();
periodic_connection.disconnect ();
if (_surface != 0) {
// These will fail if the port has gone away.
@ -1169,27 +1160,6 @@ MackieControlProtocol::update_timecode_display()
}
}
void
MackieControlProtocol::poll_session_data()
{
if (_active) {
// do all currently mapped routes
{
Glib::Mutex::Lock lm (route_signals_lock);
for (RouteSignals::iterator it = route_signals.begin(); it != route_signals.end(); ++it) {
update_automation (**it);
}
}
// and the master strip
if (master_route_signal != 0) {
update_automation (*master_route_signal);
}
update_timecode_display();
}
}
/////////////////////////////////////
// Transport Buttons
/////////////////////////////////////

View file

@ -278,13 +278,6 @@ class MackieControlProtocol
void add_port (MIDI::Port &, MIDI::Port &, int number, Mackie::MackiePort::port_type_t);
/**
Read session data and send to surface. Includes
automation from the currently active routes and
timecode displays.
*/
void poll_session_data();
// called from poll_automation to figure out which automations need to be sent
void update_automation(Mackie::RouteSignal &);
@ -317,8 +310,9 @@ class MackieControlProtocol
void port_connected_or_disconnected (std::string, std::string, bool);
bool control_in_use_timeout (Mackie::SurfacePort*, Mackie::Control *, Mackie::Control *);
bool meter_update();
sigc::connection meter_connection;
bool periodic();
sigc::connection periodic_connection;
boost::shared_ptr<Mackie::RouteSignal> master_route_signal;

View file

@ -27,6 +27,16 @@
#include "ardour/debug.h"
#include "controls.h"
#include "control_group.h"
#include "strip.h"
#include "button.h"
#include "led.h"
#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
#include "midi_byte_array.h"
#include "mackie_port.h"
@ -36,52 +46,52 @@ using namespace std;
#define NUCLEUS_DEBUG 1
MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const ControlState & state )
MIDI::byte MackieMidiBuilder::calculate_pot_value (midi_pot_mode mode, const ControlState & state)
{
// TODO do an exact calc for 0.50? To allow manually re-centering the port.
// center on or off
MIDI::byte retval = ( state.pos > 0.45 && state.pos < 0.55 ? 1 : 0 ) << 6;
MIDI::byte retval = (state.pos > 0.45 && state.pos < 0.55 ? 1 : 0) << 6;
// mode
retval |= ( mode << 4 );
retval |= (mode << 4);
// value, but only if off hasn't explicitly been set
if ( state.led_state != off )
retval += ( int(state.pos * 10.0) + 1 ) & 0x0f; // 0b00001111
if (state.led_state != off)
retval += (int(state.pos * 10.0) + 1) & 0x0f; // 0b00001111
return retval;
}
MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state, midi_pot_mode mode )
MidiByteArray MackieMidiBuilder::build_led_ring (const Pot & pot, const ControlState & state, midi_pot_mode mode )
{
return build_led_ring( pot.led_ring(), state, mode );
return build_led_ring (pot.led_ring(), state, mode);
}
MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state, midi_pot_mode mode )
MidiByteArray MackieMidiBuilder::build_led_ring (const LedRing & led_ring, const ControlState & state, midi_pot_mode mode)
{
// The other way of doing this:
// 0x30 + pot/ring number (0-7)
//, 0x30 + led_ring.ordinal() - 1
return MidiByteArray ( 3
return MidiByteArray (3
// the control type
, midi_pot_id
// the id
, 0x20 + led_ring.raw_id()
// the value
, calculate_pot_value( mode, state )
, calculate_pot_value (mode, state)
);
}
MidiByteArray MackieMidiBuilder::build_led( const Button & button, LedState ls )
MidiByteArray MackieMidiBuilder::build_led (const Button & button, LedState ls)
{
return build_led( button.led(), ls );
return build_led (button.led(), ls);
}
MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
MidiByteArray MackieMidiBuilder::build_led (const Led & led, LedState ls)
{
MIDI::byte state = 0;
switch ( ls.state() )
switch (ls.state())
{
case LedState::on: state = 0x7f; break;
case LedState::off: state = 0x00; break;
@ -89,107 +99,98 @@ MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
case LedState::flashing: state = 0x01; break;
}
return MidiByteArray ( 3
return MidiByteArray (3
, midi_button_id
, led.raw_id()
, state
);
}
MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
MidiByteArray MackieMidiBuilder::build_fader (const Fader & fader, float pos)
{
int posi = int( 0x3fff * pos );
int posi = int (0x3fff * pos);
return MidiByteArray ( 3
return MidiByteArray (3
, midi_fader_id | fader.raw_id()
// lower-order bits
, posi & 0x7f
// higher-order bits
, ( posi >> 7 )
, (posi >> 7)
);
}
MidiByteArray MackieMidiBuilder::build_meter (const Meter & meter, float val)
{
MIDI::byte segment = lrintf (val*16.0);
return MidiByteArray (2,
0xD0,
(meter.raw_id()<<3) | segment);
}
MidiByteArray MackieMidiBuilder::zero_strip( SurfacePort & port, const Strip & strip )
MidiByteArray MackieMidiBuilder::zero_strip (SurfacePort & port, const Strip & strip)
{
Group::Controls::const_iterator it = strip.controls().begin();
MidiByteArray retval;
for (; it != strip.controls().end(); ++it )
{
for (; it != strip.controls().end(); ++it) {
Control & control = **it;
if ( control.accepts_feedback() )
retval << zero_control( control );
if (control.accepts_feedback())
retval << zero_control (control);
}
// These must have sysex headers
/* 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 (port, strip, 0);
retval << strip_display_blank (port, strip, 1);
}
return retval;
}
MidiByteArray MackieMidiBuilder::zero_control( const Control & control )
MidiByteArray MackieMidiBuilder::zero_control (const Control & control)
{
switch( control.type() ) {
switch (control.type()) {
case Control::type_button:
return build_led( (Button&)control, off );
return build_led ((Button&)control, off);
case Control::type_led:
return build_led( (Led&)control, off );
return build_led ((Led&)control, off);
case Control::type_fader:
return build_fader( (Fader&)control, 0.0 );
return build_fader ((Fader&)control, 0.0);
case Control::type_pot:
return build_led_ring( dynamic_cast<const Pot&>( control ), off );
return build_led_ring (dynamic_cast<const Pot&> (control), off);
case Control::type_led_ring:
return build_led_ring( dynamic_cast<const LedRing&>( control ), off );
return build_led_ring (dynamic_cast<const LedRing&> (control), off);
case Control::type_meter:
return build_meter (dynamic_cast<const Meter&>(control), 0.0);
return const_cast<Meter&>(dynamic_cast<const Meter&>(control)).update_message (0.0);
default:
ostringstream os;
os << "Unknown control type " << control << " in Strip::zero_control";
throw MackieControlException( os.str() );
throw MackieControlException (os.str());
}
}
char translate_seven_segment( char achar )
char translate_seven_segment (char achar)
{
achar = toupper( achar );
if ( achar >= 0x40 && achar <= 0x60 )
achar = toupper (achar);
if (achar >= 0x40 && achar <= 0x60)
return achar - 0x40;
else if ( achar >= 0x21 && achar <= 0x3f )
else if (achar >= 0x21 && achar <= 0x3f)
return achar;
else
return 0x00;
}
MidiByteArray MackieMidiBuilder::two_char_display( const std::string & msg, const std::string & dots )
MidiByteArray MackieMidiBuilder::two_char_display (const std::string & msg, const std::string & dots)
{
if ( msg.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: msg must be exactly 2 characters" );
if ( dots.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: dots must be exactly 2 characters" );
if (msg.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: msg must be exactly 2 characters");
if (dots.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: dots must be exactly 2 characters");
MidiByteArray bytes( 5, 0xb0, 0x4a, 0x00, 0x4b, 0x00 );
MidiByteArray bytes (5, 0xb0, 0x4a, 0x00, 0x4b, 0x00);
// chars are understood by the surface in right-to-left order
// could also exchange the 0x4a and 0x4b, above
bytes[4] = translate_seven_segment( msg[0] ) + ( dots[0] == '.' ? 0x40 : 0x00 );
bytes[2] = translate_seven_segment( msg[1] ) + ( dots[1] == '.' ? 0x40 : 0x00 );
bytes[4] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
bytes[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
return bytes;
}
@ -198,16 +199,16 @@ MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std
{
ostringstream os;
os << setfill('0') << setw(2) << value % 100;
return two_char_display( os.str() );
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 (SurfacePort & port, const Strip & strip, unsigned int line_number)
{
// 6 spaces, not 7 because strip_display adds a space where appropriate
return strip_display( port, strip, line_number, " " );
return strip_display (port, strip, line_number, " ");
}
MidiByteArray MackieMidiBuilder::strip_display (SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line )
MidiByteArray MackieMidiBuilder::strip_display (SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line)
{
assert (line_number <= 1);
@ -221,7 +222,7 @@ MidiByteArray MackieMidiBuilder::strip_display (SurfacePort & port, const Strip
// code for display
retval << 0x12;
// offset (0 to 0x37 first line, 0x38 to 0x6f for second line )
// offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
retval << (index * 7 + (line_number * 0x38));
// ascii data to display
@ -253,20 +254,26 @@ 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 (SurfacePort & port, const std::string & timecode, const std::string & last_timecode)
{
// if there's no change, send nothing, not even sysex header
if ( timecode == last_timecode ) return MidiByteArray();
if (timecode == last_timecode) return MidiByteArray();
// length sanity checking
string local_timecode = timecode;
// truncate to 10 characters
if ( local_timecode.length() > 10 ) local_timecode = local_timecode.substr( 0, 10 );
if (local_timecode.length() > 10) {
local_timecode = local_timecode.substr (0, 10);
}
// pad to 10 characters
while ( local_timecode.length() < 10 ) local_timecode += " ";
while (local_timecode.length() < 10) {
local_timecode += " ";
}
// find the suffix of local_timecode that differs from last_timecode
std::pair<string::const_iterator,string::iterator> pp = mismatch( last_timecode.begin(), last_timecode.end(), local_timecode.begin() );
std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
MidiByteArray retval;
@ -278,10 +285,9 @@ MidiByteArray MackieMidiBuilder::timecode_display( SurfacePort & port, const std
// translate characters. These are sent in reverse order of display
// hence the reverse iterators
string::reverse_iterator rend = reverse_iterator<string::iterator>( pp.second );
for ( string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it )
{
retval << translate_seven_segment( *it );
string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
retval << translate_seven_segment (*it);
}
// sysex trailer

View file

@ -26,6 +26,13 @@ namespace Mackie
{
class SurfacePort;
class Button;
class Meter;
class Fader;
class Jog;
class Pot;
class Led;
class LedRing;
/**
This knows how to build midi messages given a control and
@ -63,8 +70,6 @@ public:
MidiByteArray build_fader( const Fader & fader, float pos );
MidiByteArray build_meter (const Meter& meter, float val);
/// 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 );

View file

@ -15,6 +15,12 @@
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"
@ -23,9 +29,11 @@
#include "controls.h"
#include "surface.h"
#include <glibmm/main.h>
#include <boost/shared_array.hpp>
#include "fader.h"
#include "button.h"
#include "strip.h"
#include "pot.h"
#include "control_group.h"
#include "midi++/types.h"
#include "midi++/port.h"
@ -35,8 +43,6 @@
#include "i18n.h"
#include <sstream>
using namespace std;
using namespace Mackie;
using namespace ARDOUR;

View file

@ -0,0 +1,92 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cmath>
#include "meter.h"
#include "surface.h"
#include "surface_port.h"
#include "control_group.h"
using namespace Mackie;
Control*
Meter::factory (Surface& surface, int id, int ordinal, const char* name, Group& group)
{
Meter* m = new Meter (id, ordinal, name, group);
surface.meters[id] = m;
surface.controls.push_back (m);
group.add (*m);
return m;
}
MidiByteArray
Meter::update_message (float dB)
{
float def = 0.0f; /* Meter deflection %age */
if (dB < -70.0f) {
def = 0.0f;
} else if (dB < -60.0f) {
def = (dB + 70.0f) * 0.25f;
} else if (dB < -50.0f) {
def = (dB + 60.0f) * 0.5f + 2.5f;
} else if (dB < -40.0f) {
def = (dB + 50.0f) * 0.75f + 7.5f;
} else if (dB < -30.0f) {
def = (dB + 40.0f) * 1.5f + 15.0f;
} else if (dB < -20.0f) {
def = (dB + 30.0f) * 2.0f + 30.0f;
} else if (dB < 6.0f) {
def = (dB + 20.0f) * 2.5f + 50.0f;
} else {
def = 115.0f;
}
/* 115 is the deflection %age that would be
when dB=6.0. this is an arbitrary
endpoint for our scaling.
*/
MidiByteArray msg;
if (def > 100.0f) {
if (!overload_on) {
overload_on = true;
msg << MidiByteArray (2, 0xd0, (raw_id() << 4) | 0xe);
}
} else {
if (overload_on) {
overload_on = false;
msg << MidiByteArray (2, 0xd0, (raw_id() << 4) | 0xf);
}
}
/* we can use up to 13 segments */
int segment = lrintf ((def/115.0) * 13.0);
if (last_segment_value_sent != segment) {
last_segment_value_sent = segment;
std::cerr << "Meter ID " << raw_id() << " as byte " << (((int) raw_id() << 4) | segment) << std::endl;
msg << MidiByteArray (2, 0xD0, (raw_id()<<4) | segment);
}
return msg;
}

View file

@ -0,0 +1,52 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_mackie_control_protocol_meter_h__
#define __ardour_mackie_control_protocol_meter_h__
#include "controls.h"
#include "midi_byte_array.h"
namespace Mackie {
class SurfacePort;
class Meter : public Control
{
public:
Meter (int id, int ordinal, std::string name, Group & group)
: Control (id, ordinal, name, group)
, last_segment_value_sent (-1)
, overload_on (false) {}
virtual type_t type() const { return type_meter; }
MidiByteArray update_message (float dB);
static Control* factory (Surface&, int id, int ordinal, const char*, Group&);
int last_segment_value_sent;
private:
bool overload_on;
};
}
#endif /* __ardour_mackie_control_protocol_meter_h__ */

View file

@ -0,0 +1,28 @@
#ifndef __ardour_mackie_control_protocol_pot_h__
#define __ardour_mackie_control_protocol_pot_h__
#include "controls.h"
#include "ledring.h"
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) {}
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&);
private:
LedRing _led_ring;
};
}
#endif /* __ardour_mackie_control_protocol_pot_h__ */

View file

@ -15,7 +15,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "route_signal.h"
#include <stdexcept>
#include "ardour/route.h"
#include "ardour/track.h"
@ -24,8 +25,8 @@
#include "ardour/session_object.h" // for Properties::name
#include "mackie_control_protocol.h"
#include <stdexcept>
#include "route_signal.h"
#include "strip.h"
using namespace ARDOUR;
using namespace Mackie;

View file

@ -0,0 +1,208 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sstream>
#include <stdint.h>
#include "strip.h"
#include "button.h"
#include "led.h"
#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
using namespace Mackie;
using namespace std;
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 */
}
Strip::Strip (Surface& surface, const std::string& name, int index, int unit_index, StripControlDefinition* ctls)
: Group (name)
, _solo (0)
, _recenable (0)
, _mute (0)
, _select (0)
, _vselect (0)
, _fader_touch (0)
, _vpot (0)
, _gain (0)
, _index (index)
{
/* 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 + unit_index, unit_index+1, ctls[i].name, *this);
}
}
/**
TODO could optimise this to use enum, but it's only
called during the protocol class instantiation.
*/
void Strip::add (Control & control)
{
Group::add (control);
if (control.name() == "gain") {
_gain = reinterpret_cast<Fader*>(&control);
} else if (control.name() == "vpot") {
_vpot = reinterpret_cast<Pot*>(&control);
} else if (control.name() == "recenable") {
_recenable = reinterpret_cast<Button*>(&control);
} else if (control.name() == "solo") {
_solo = reinterpret_cast<Button*>(&control);
} else if (control.name() == "mute") {
_mute = reinterpret_cast<Button*>(&control);
} else if (control.name() == "select") {
_select = reinterpret_cast<Button*>(&control);
} else if (control.name() == "vselect") {
_vselect = reinterpret_cast<Button*>(&control);
} else if (control.name() == "fader_touch") {
_fader_touch = reinterpret_cast<Button*>(&control);
} else if (control.name() == "meter") {
_meter = reinterpret_cast<Meter*>(&control);
} else if (control.type() == Control::type_led || control.type() == Control::type_led_ring) {
// relax
} else {
ostringstream os;
os << "Strip::add: unknown control type " << control;
throw MackieControlException (os.str());
}
}
Fader&
Strip::gain()
{
if (_gain == 0) {
throw MackieControlException ("gain is null");
}
return *_gain;
}
Pot&
Strip::vpot()
{
if (_vpot == 0) {
throw MackieControlException ("vpot is null");
}
return *_vpot;
}
Button&
Strip::recenable()
{
if (_recenable == 0) {
throw MackieControlException ("recenable is null");
}
return *_recenable;
}
Button&
Strip::solo()
{
if (_solo == 0) {
throw MackieControlException ("solo is null");
}
return *_solo;
}
Button&
Strip::mute()
{
if (_mute == 0) {
throw MackieControlException ("mute is null");
}
return *_mute;
}
Button&
Strip::select()
{
if (_select == 0) {
throw MackieControlException ("select is null");
}
return *_select;
}
Button&
Strip::vselect()
{
if (_vselect == 0) {
throw MackieControlException ("vselect is null");
}
return *_vselect;
}
Button&
Strip::fader_touch()
{
if (_fader_touch == 0) {
throw MackieControlException ("fader_touch is null");
}
return *_fader_touch;
}
Meter&
Strip::meter()
{
if (_meter == 0) {
throw MackieControlException ("meter is null");
}
return *_meter;
}
std::ostream & Mackie::operator << (std::ostream & os, const Strip & strip)
{
os << typeid (strip).name();
os << " { ";
os << "has_solo: " << boolalpha << strip.has_solo();
os << ", ";
os << "has_recenable: " << boolalpha << strip.has_recenable();
os << ", ";
os << "has_mute: " << boolalpha << strip.has_mute();
os << ", ";
os << "has_select: " << boolalpha << strip.has_select();
os << ", ";
os << "has_vselect: " << boolalpha << strip.has_vselect();
os << ", ";
os << "has_fader_touch: " << boolalpha << strip.has_fader_touch();
os << ", ";
os << "has_vpot: " << boolalpha << strip.has_vpot();
os << ", ";
os << "has_gain: " << boolalpha << strip.has_gain();
os << " }";
return os;
}

View file

@ -0,0 +1,89 @@
#ifndef __ardour_mackie_control_protocol_strip_h__
#define __ardour_mackie_control_protocol_strip_h__
#include <string>
#include <iostream>
#include "control_group.h"
namespace Mackie {
class Control;
class Surface;
class Button;
class Pot;
class Fader;
class Meter;
struct StripControlDefinition {
const char* name;
uint32_t base_id;
Control* (*factory)(Surface&, int index, int ordinal, const char* name, Group&);
};
struct GlobalControlDefinition {
const char* name;
uint32_t id;
Control* (*factory)(Surface&, int index, int ordinal, const char* name, Group&);
const char* group_name;
};
/**
This is the set of controls that make up a strip.
*/
class Strip : public Group
{
public:
Strip (const std::string& name, int index); /* master strip only */
Strip (Surface&, const std::string & name, int index, int unit_index, StripControlDefinition* ctls);
virtual bool is_strip() const { return true; }
virtual void add (Control & control);
int index() const { return _index; } // zero based
Button & solo();
Button & recenable();
Button & mute();
Button & select();
Button & vselect();
Button & fader_touch();
Pot & vpot();
Fader & gain();
Meter& meter ();
bool has_solo() const { return _solo != 0; }
bool has_recenable() const { return _recenable != 0; }
bool has_mute() const { return _mute != 0; }
bool has_select() const { return _select != 0; }
bool has_vselect() const { return _vselect != 0; }
bool has_fader_touch() const { return _fader_touch != 0; }
bool has_vpot() const { return _vpot != 0; }
bool has_gain() const { return _gain != 0; }
bool has_meter() const { return _meter != 0; }
private:
Button* _solo;
Button* _recenable;
Button* _mute;
Button* _select;
Button* _vselect;
Button* _fader_touch;
Pot* _vpot;
Fader* _gain;
Meter* _meter;
int _index;
};
std::ostream & operator << (std::ostream &, const Strip &);
class MasterStrip : public Strip
{
public:
MasterStrip (const std::string & name, int index)
: Strip (name, index) {}
virtual bool is_master() const { return true; }
};
}
#endif /* __ardour_mackie_control_protocol_strip_h__ */

View file

@ -6,8 +6,19 @@
#include "ardour/debug.h"
#include "mackie_button_handler.h"
#include "control_group.h"
#include "surface_port.h"
#include "surface.h"
#include "strip.h"
#include "strip.h"
#include "button.h"
#include "led.h"
#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
using namespace std;
using namespace PBD;

View file

@ -11,6 +11,13 @@ namespace Mackie
class MackieButtonHandler;
class SurfacePort;
class MackieMidiBuilder;
class Button;
class Meter;
class Fader;
class Jog;
class Pot;
class Led;
class LedRing;
/**
This represents an entire control surface, made up of Groups,

View file

@ -33,8 +33,10 @@ def build(bld):
mackie_midi_builder.cc
mackie_port.cc
mackie_surface.cc
meter.cc
midi_byte_array.cc
route_signal.cc
strip.cc
surface.cc
surface_port.cc
types.cc