, _led (id, name + "_led", group) {}
virtual Led & led() { return _led; }
-
- virtual type_t type() const { return type_button; };
+ MidiByteArray zero() { return _led.set_state (off); }
+
MidiByteArray update_message () const;
static Control* factory (Surface&, int id, const char*, Group&);
#include "controls.h"
#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"
os << " { ";
os << "name: " << control.name();
os << ", ";
- os << "id: " << "0x" << setw(4) << setfill('0') << hex << control.id() << setfill(' ');
- os << ", ";
- os << "type: " << "0x" << setw(2) << setfill('0') << hex << control.type() << setfill(' ');
- os << ", ";
os << "raw_id: " << "0x" << setw(2) << setfill('0') << hex << control.raw_id() << setfill(' ');
os << ", ";
os << "group: " << control.group().name();
return os;
}
-Control*
-Pot::factory (Surface& surface, int id, const char* name, Group& group)
-{
- Pot* p = new Pot (id, name, group);
- surface.pots[id] = p;
- surface.controls.push_back (p);
- group.add (*p);
- return p;
-}
-
Control*
Jog::factory (Surface& surface, int id, const char* name, Group& group)
{
#include "pbd/signals.h"
#include "mackie_control_exception.h"
+#include "midi_byte_array.h"
namespace Mackie
{
class Strip;
class Group;
-class Led;
class Surface;
class Control
{
public:
- enum type_t {
- type_led,
- type_led_ring,
- type_fader = 0xe0,
- type_button = 0x90,
- type_pot = 0xb0,
- type_meter = 0xd0
- };
-
enum base_id_t {
- fader_base_id = 0x0,
- pot_base_id = 0x10,
+ fader_base_id = 0xe0,
+ pot_base_id = 0x30,
jog_base_id = 0x3c,
fader_touch_button_base_id = 0xe0,
vselect_button_base_id = 0x20,
Control (int id, std::string name, Group& group);
virtual ~Control() {}
- virtual Led & led() { throw MackieControlException ("no led available"); }
-
- /// type() << 8 + midi id of the control. This
- /// provides a unique id for any control on the surface.
- int id() const { return (type() << 8) + _id; }
-
/// the value of the second bytes of the message. It's
/// the id of the control, but only guaranteed to be
/// unique within the control type.
const std::string & name() const { return _name; }
Group & group() const { return _group; }
+
virtual bool accepts_feedback() const { return true; }
- virtual type_t type() const = 0;
-
- /// Return true if this control is the one and only Jog Wheel
- virtual bool is_jog() const { return false; }
-
bool in_use () const;
void set_in_use (bool);
/// Keep track of the timeout so it can be updated with more incoming events
sigc::connection in_use_connection;
+ virtual MidiByteArray zero() = 0;
+
/** If we are doing an in_use timeout for a fader without touch, this
* is its touch button control; otherwise 0.
*/
return f;
}
+MidiByteArray
+Fader::set_position (float normalized)
+{
+ position = normalized;
+ return update_message ();
+}
+
+MidiByteArray
+Fader::update_message ()
+{
+ int posi = int (0x3fff * position);
+ return MidiByteArray (3, raw_id(), posi & 0x7f, posi >> 7);
+}
public:
Fader (int id, std::string name, Group & group)
: Control (id, name, group)
+ , position (0.0)
{
}
+
+ MidiByteArray set_position (float);
+ MidiByteArray zero() { return set_position (0.0); }
- virtual type_t type() const { return type_fader; }
+ MidiByteArray update_message ();
static Control* factory (Surface&, int id, const char*, Group&);
+
+ private:
+ float position;
};
}
{
}
- virtual bool is_jog() const { return true; }
+ MidiByteArray zero() { return MidiByteArray(); }
static Control* factory (Surface&, int id, const char*, Group&);
};
}
Led & led() { return *this; }
- type_t type() const { return type_led; }
MidiByteArray set_state (LedState);
+
+ MidiByteArray zero() { return set_state (off); }
static Control* factory (Surface&, int id, const char*, Group&);
+++ /dev/null
-/*
- 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, std::string name, Group & group)
- : Led (id, name, group)
- {
- }
-
- virtual type_t type() const { return type_led_ring; }
-};
-
-}
-
-#endif
#include "midi_byte_array.h"
#include "mackie_control_exception.h"
-#include "mackie_midi_builder.h"
#include "surface_port.h"
#include "surface.h"
(*s)->periodic ();
}
- update_timecode_display();
-
return true;
}
#include "midi++/types.h"
#include "ardour/types.h"
-
#include "control_protocol/control_protocol.h"
+
+#include "types.h"
#include "midi_byte_array.h"
#include "controls.h"
#include "mackie_jog_wheel.h"
-#include "mackie_midi_builder.h"
#include "timer.h"
namespace MIDI {
int _modifier_state;
- Mackie::MackieMidiBuilder builder;
-
typedef std::list<GSource*> PortSources;
PortSources port_sources;
#include "ardour/session.h"
#include "mackie_jog_wheel.h"
-
#include "mackie_control_protocol.h"
#include "surface_port.h"
#include "controls.h"
+++ /dev/null
-/*
- Copyright (C) 2006,2007 John Anderson
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-#include "mackie_midi_builder.h"
-
-#include <typeinfo>
-#include <sstream>
-#include <iomanip>
-#include <algorithm>
-#include <cmath>
-
-#include "pbd/compose.h"
-
-#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 "surface.h"
-#include "surface_port.h"
-
-using namespace PBD;
-using namespace Mackie;
-using namespace std;
-
-#define NUCLEUS_DEBUG 1
-
-MIDI::byte MackieMidiBuilder::calculate_pot_value (midi_pot_mode mode, const ControlState & state)
-{
- // TODO do an exact calc for 0.50? To allow manually re-centering the port.
-
- // center on or off
- MIDI::byte retval = (state.pos > 0.45 && state.pos < 0.55 ? 1 : 0) << 6;
-
- // mode
- retval |= (mode << 4);
-
- // value, but only if off hasn't explicitly been set
- if (state.led_state != off)
- retval += (int(state.pos * 10.0) + 1) & 0x0f; // 0b00001111
-
- return retval;
-}
-
-MidiByteArray MackieMidiBuilder::build_led_ring (const Pot & pot, const ControlState & state, midi_pot_mode 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)
-{
- // The other way of doing this:
- // 0x30 + pot/ring number (0-7)
- //, 0x30 + led_ring.ordinal() - 1
- return MidiByteArray (3
- // the control type
- , midi_pot_id
- // the id
- , 0x20 + led_ring.raw_id()
- // the value
- , calculate_pot_value (mode, state)
- );
-}
-
-MidiByteArray MackieMidiBuilder::build_fader (const Fader & fader, float pos)
-{
- int posi = int (0x3fff * pos);
-
- return MidiByteArray (3
- , midi_fader_id | fader.raw_id()
- // lower-order bits
- , posi & 0x7f
- // higher-order bits
- , (posi >> 7)
- );
-}
-
-MidiByteArray MackieMidiBuilder::zero_strip (Surface& surface, const Strip & strip)
-{
- Group::Controls::const_iterator it = strip.controls().begin();
- MidiByteArray retval;
-
- for (; it != strip.controls().end(); ++it) {
- Control & control = **it;
- if (control.accepts_feedback())
- retval << zero_control (control);
- }
-
- // 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 (surface, strip, 0);
- retval << strip_display_blank (surface, strip, 1);
- }
-
- return retval;
-}
-
-MidiByteArray MackieMidiBuilder::zero_control (Control & control)
-{
- switch (control.type()) {
- case Control::type_button:
- return control.led().set_state (off);
-
- case Control::type_led:
- return dynamic_cast<Led&>(control).set_state (off);
-
- case Control::type_fader:
- return build_fader ((Fader&)control, 0.0);
-
- case Control::type_pot:
- return build_led_ring (dynamic_cast<const Pot&> (control), off);
-
- case Control::type_led_ring:
- return build_led_ring (dynamic_cast<const LedRing&> (control), off);
-
- case Control::type_meter:
- 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());
- }
-}
-
-char translate_seven_segment (char achar)
-{
- achar = toupper (achar);
- if (achar >= 0x40 && achar <= 0x60)
- return achar - 0x40;
- else if (achar >= 0x21 && achar <= 0x3f)
- return achar;
- else
- return 0x00;
-}
-
-MidiByteArray MackieMidiBuilder::two_char_display (const std::string & msg, const std::string & dots)
-{
- if (msg.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: msg must be exactly 2 characters");
- if (dots.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: dots must be exactly 2 characters");
-
- MidiByteArray bytes (6, 0xb0, 0x4a, 0x00, 0xb0, 0x4b, 0x00);
-
- // chars are understood by the surface in right-to-left order
- // could also exchange the 0x4a and 0x4b, above
- bytes[5] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
- bytes[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
-
- return bytes;
-}
-
-MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std::string & /*dots*/)
-{
- ostringstream os;
- os << setfill('0') << setw(2) << value % 100;
- return two_char_display (os.str());
-}
-
-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 (surface, strip, line_number, " ");
-}
-
-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() % 8;
-
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display index: %1, line %2 = %3\n", strip.index(), line_number, line));
-
- // sysex header
- retval << surface.sysex_hdr();
-
- // code for display
- retval << 0x12;
- // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
- retval << (index * 7 + (line_number * 0x38));
-
- // ascii data to display
- retval << line;
- // pad with " " out to 6 chars
- for (int i = line.length(); i < 6; ++i) {
- retval << ' ';
- }
-
- // column spacer, unless it's the right-hand column
- if (strip.index() < 7) {
- retval << ' ';
- }
-
- // sysex trailer
- retval << MIDI::eox;
-
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
-
- return retval;
-}
-
-MidiByteArray MackieMidiBuilder::all_strips_display (SurfacePort & /*port*/, std::vector<std::string> & /*lines1*/, std::vector<std::string> & /*lines2*/)
-{
- MidiByteArray retval;
- retval << 0x12 << 0;
- // NOTE remember max 112 bytes per message, including sysex headers
- retval << "Not working yet";
- return retval;
-}
-
-MidiByteArray
-MackieMidiBuilder::timecode_display (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();
-
- // length sanity checking
- string local_timecode = timecode;
-
- // truncate to 10 characters
- if (local_timecode.length() > 10) {
- local_timecode = local_timecode.substr (0, 10);
- }
-
- // pad to 10 characters
- while (local_timecode.length() < 10) {
- local_timecode += " ";
- }
-
- // find the suffix of local_timecode that differs from last_timecode
- std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
-
- MidiByteArray retval;
-
- // sysex header
- retval << surface.sysex_hdr();
-
- // code for timecode display
- retval << 0x10;
-
- // translate characters. These are sent in reverse order of display
- // hence the reverse iterators
- string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
- for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
- retval << translate_seven_segment (*it);
- }
-
- // sysex trailer
- retval << MIDI::eox;
-
- return retval;
-}
+++ /dev/null
-/*
- Copyright (C) 2006,2007 John Anderson
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-#ifndef mackie_midi_builder_h
-#define mackie_midi_builder_h
-
-#include "midi_byte_array.h"
-#include "types.h"
-#include "controls.h"
-
-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
- a state.
-*/
-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,
- };
-
- /**
- The LED rings have these modes.
- */
- enum midi_pot_mode {
- midi_pot_mode_dot = 0,
- midi_pot_mode_boost_cut = 1,
- midi_pot_mode_wrap = 2,
- midi_pot_mode_spread = 3,
- };
-
- MidiByteArray build_led_ring (const Pot & pot, const ControlState &, 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_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 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 (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 = " ");
-
- /**
- 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 (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 (Surface &, const Strip & strip, unsigned int line_number, const std::string & line);
-
- /// 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);
-
-protected:
- static MIDI::byte calculate_pot_value (midi_pot_mode mode, const ControlState &);
-};
-
-}
-
-#endif
, last_segment_value_sent (-1)
, overload_on (false) {}
- virtual type_t type() const { return type_meter; }
-
MidiByteArray update_message (float dB);
+ MidiByteArray zero() { return update_message (-99999999.0); }
+
static Control* factory (Surface&, int id, const char*, Group&);
int last_segment_value_sent;
--- /dev/null
+/*
+ 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 "pot.h"
+#include "surface.h"
+#include "control_group.h"
+
+using namespace Mackie;
+
+Control*
+Pot::factory (Surface& surface, int id, const char* name, Group& group)
+{
+ Pot* p = new Pot (id, name, group);
+ surface.pots[id] = p;
+ surface.controls.push_back (p);
+ group.add (*p);
+ return p;
+}
+
+MidiByteArray
+Pot::set_mode (Pot::Mode m)
+{
+ mode = m;
+ return update_message ();
+}
+
+MidiByteArray
+Pot::set_onoff (bool onoff)
+{
+ on = onoff;
+ return update_message ();
+}
+
+MidiByteArray
+Pot::set_value (float normalized)
+{
+ value = normalized;
+ return update_message ();
+}
+
+MidiByteArray
+Pot::set_all (float val, bool onoff, Mode m)
+{
+ value = val;
+ on = onoff;
+ mode = m;
+ return update_message ();
+}
+
+MidiByteArray
+Pot::update_message ()
+{
+ // TODO do an exact calc for 0.50? To allow manually re-centering the port.
+
+ // center on or off
+ MIDI::byte msg = (value > 0.45 && value < 0.55 ? 1 : 0) << 6;
+
+ // mode
+ msg |= (mode << 4);
+
+ // value, but only if off hasn't explicitly been set
+
+ if (on) {
+ msg += (lrintf (value * 10.0) + 1) & 0x0f; // 0b00001111
+ }
+
+ return MidiByteArray (3, 0xb0, raw_id(), msg);
+
+}
+
#define __ardour_mackie_control_protocol_pot_h__
#include "controls.h"
-#include "ledring.h"
namespace Mackie {
class Pot : public Control
{
public:
+ enum Mode {
+ dot = 0,
+ boost_cut = 1,
+ wrap = 2,
+ spread = 3,
+ };
+
Pot (int id, std::string name, Group & group)
: Control (id, name, group)
- , _led_ring (id, name + "_ring", group) {}
+ , value (0.0)
+ , mode (dot)
+ , on (true) {}
- virtual type_t type() const { return type_pot; }
+ MidiByteArray set_mode (Mode);
+ MidiByteArray set_value (float);
+ MidiByteArray set_onoff (bool);
+ MidiByteArray set_all (float, bool, Mode);
- virtual const LedRing & led_ring() const {return _led_ring; }
+ MidiByteArray zero() { return set_value (0.0); }
+
+ MidiByteArray update_message ();
static Control* factory (Surface&, int id, const char*, Group&);
-private:
- LedRing _led_ring;
+ private:
+ float value;
+ Mode mode;
+ bool on;
};
}
#include "surface.h"
#include "button.h"
#include "led.h"
-#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
_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());
+ // relax
}
}
void
Strip::notify_solo_changed ()
{
- if (_route) {
- Button& button = solo();
- _surface->write (button.led().set_state (_route->soloed() ? on : off));
+ if (_route && _solo) {
+ _surface->write (_solo->led().set_state (_route->soloed() ? on : off));
}
}
void
Strip::notify_mute_changed ()
{
- if (_route) {
- Button & button = mute();
- _surface->write (button.led().set_state (_route->muted() ? on : off));
+ if (_route && _mute) {
+ _surface->write (_mute->led().set_state (_route->muted() ? on : off));
}
}
void
Strip::notify_record_enable_changed ()
{
- if (_route) {
- Button & button = recenable();
- _surface->write (button.led().set_state (_route->record_enabled() ? on : off));
+ if (_route && _recenable) {
+ _surface->write (_recenable->led().set_state (_route->record_enabled() ? on : off));
}
}
if (_route) {
Fader & fader = gain();
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("route %1 gain change, update fader %2 on port %3 in-use ? %4\n",
- _route->name(),
- fader.raw_id(),
- _surface->port().output_port().name(),
- fader.in_use()));
if (!fader.in_use()) {
- float gain_value = gain_to_slider_position (_route->gain_control()->get_value());
+ float position = 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;
- } else {
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader not updated because gain still equals %1\n", gain_value));
+ if (force_update || position != _last_gain_written) {
+ _surface->write (fader.set_position (position));
+ _last_gain_written = position;
}
}
}
line1 = PBD::short_version (fullname, 6);
}
- _surface->write (builder.strip_display (*_surface, *this, 0, line1));
- _surface->write (builder.strip_display_blank (*_surface, *this, 1));
+ _surface->write (display (0, line1));
+ _surface->write (blank_display (1));
}
}
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);
+
+ MidiByteArray bytes = pot.set_all (pos, true, Pot::dot);
+
// check that something has actually changed
if (force_update || bytes != _last_pan_written)
{
_last_pan_written = bytes;
}
} else {
- _surface->write (builder.zero_control (pot));
+ _surface->write (pot.zero());
}
}
}
bool
Strip::handle_button (SurfacePort & port, Control & control, ButtonState bs)
{
+ Button* button = dynamic_cast<Button*>(&control);
+
+ if (!button) {
+ return false;
+ }
+
if (!_route) {
// no route so always switch the light off
// because no signals will be emitted by a non-route
- _surface->write (control.led().set_state (off));
+ _surface->write (button->led().set_state (off));
return false;
}
float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
_surface->write (meter().update_message (dB));
}
+
+MidiByteArray
+Strip::zero ()
+{
+ MidiByteArray retval;
+
+ for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
+ retval << (*it)->zero ();
+ }
+
+ retval << blank_display (0);
+ retval << blank_display (1);
+
+ return retval;
+}
+
+MidiByteArray
+Strip::blank_display (uint32_t line_number)
+{
+ return display (line_number, string());
+}
+
+MidiByteArray
+Strip::display (uint32_t line_number, const std::string& line)
+{
+ assert (line_number <= 1);
+
+ MidiByteArray retval;
+
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
+
+ // sysex header
+ retval << _surface->sysex_hdr();
+
+ // code for display
+ retval << 0x12;
+ // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
+ retval << (_index * 7 + (line_number * 0x38));
+
+ // ascii data to display
+ retval << line;
+ // pad with " " out to 6 chars
+ for (int i = line.length(); i < 6; ++i) {
+ retval << ' ';
+ }
+
+ // column spacer, unless it's the right-hand column
+ if (_index < 7) {
+ retval << ' ';
+ }
+
+ // sysex trailer
+ retval << MIDI::eox;
+
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
+
+ return retval;
+}
#include <iostream>
#include "pbd/property_basics.h"
+#include "pbd/signals.h"
#include "control_group.h"
-#include "mackie_midi_builder.h"
+#include "types.h"
+#include "midi_byte_array.h"
namespace ARDOUR {
class Route;
class Pot;
class Fader;
class Meter;
+class SurfacePort;
struct StripControlDefinition {
const char* name;
void periodic ();
+ MidiByteArray display (uint32_t line_number, const std::string&);
+ MidiByteArray blank_display (uint32_t line_number);
+ MidiByteArray zero ();
+
private:
Button* _solo;
Button* _recenable;
int _index;
Surface* _surface;
- MackieMidiBuilder builder;
-
boost::shared_ptr<ARDOUR::Route> _route;
PBD::ScopedConnectionList route_connections;
#include "surface_port.h"
#include "surface.h"
#include "strip.h"
-#include "mackie_midi_builder.h"
#include "mackie_control_protocol.h"
#include "mackie_jog_wheel.h"
#include "strip.h"
#include "button.h"
#include "led.h"
-#include "ledring.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
}
}
-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)
{
{
if (current_bank == 0) {
// send Ar. to 2-char display on the master
- _port->write (builder.two_char_display ("Ar", ".."));
+ _port->write (two_char_display ("Ar", ".."));
} else {
// write the current first remote_id to the 2-char display
- _port->write (builder.two_char_display (current_bank));
+ _port->write (two_char_display (current_bank));
}
}
Control* control = controls_by_name["jog"];
if (control) {
- _port->write (builder.build_led_ring (*(dynamic_cast<Pot*> (control)), off));
+ Pot* pot = dynamic_cast<Pot*> (control);
+ if (pot) {
+ _port->write (pot->set_onoff (false));
+ }
}
}
// the state of the controls on the surface is usually updated
// from UI events.
- switch (control.type()) {
- case Control::type_fader:
+ Fader* fader = dynamic_cast<Fader*> (&control);
+ Button* button = dynamic_cast<Button*> (&control);
+ Pot* pot = dynamic_cast<Pot*> (&control);
+
+ if (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
// 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));
+ _port->write (fader->set_position (state.pos));
}
- break;
-
- case Control::type_button:
+ }
+
+ if (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()));
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", control.raw_id()));
_mcp.handle_button_event (*this, dynamic_cast<Button&>(control), state.button_state);
}
- break;
-
+ }
// pot (jog wheel, external control)
- case Control::type_pot:
+
+ if (pot) {
if (strip) {
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip pot %1\n", control.id()));
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip pot %1\n", control.raw_id()));
if (route) {
boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
// pan for mono input routes, or stereo linked panners
}
} 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));
+ Pot* pot = dynamic_cast<Pot*> (&control);
+ if (pot) {
+ _port->write (pot->set_onoff (false));
+ }
}
} else {
- if (control.is_jog()) {
+ JogWheel* wheel = dynamic_cast<JogWheel*> (pot);
+ if (wheel) {
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", state.ticks));
- if (_jog_wheel) {
- _jog_wheel->jog_event (*_port, control, state);
- }
+ 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;
}
}
// zero all strips
for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
- _port->write (builder.zero_strip (*this, **it));
+ _port->write ((*it)->zero());
}
// turn off global buttons and leds
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));
+ _port->write (control.zero());
}
}
// any hardware-specific stuff
// clear 2-char display
- _port->write (builder.two_char_display ("LC"));
+ _port->write (two_char_display (" "));
// and the led ring for the master strip
blank_jog_ring ();
for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
(*s)->periodic ();
}
+
}
void
{
switch (state) {
case JogWheel::zoom:
- _port->write (builder.two_char_display ("Zm"));
+ _port->write (two_char_display ("Zm"));
break;
case JogWheel::scroll:
- _port->write (builder.two_char_display ("Sc"));
+ _port->write (two_char_display ("Sc"));
break;
case JogWheel::scrub:
- _port->write (builder.two_char_display ("Sb"));
+ _port->write (two_char_display ("Sb"));
break;
case JogWheel::shuttle:
- _port->write (builder.two_char_display ("Sh"));
+ _port->write (two_char_display ("Sh"));
break;
case JogWheel::speed:
- _port->write (builder.two_char_display ("Sp"));
+ _port->write (two_char_display ("Sp"));
break;
case JogWheel::select:
- _port->write (builder.two_char_display ("Se"));
+ _port->write (two_char_display ("Se"));
break;
}
}
(*s)->set_route (*r);
}
}
+
+static char translate_seven_segment (char achar)
+{
+ achar = toupper (achar);
+ if (achar >= 0x40 && achar <= 0x60)
+ return achar - 0x40;
+ else if (achar >= 0x21 && achar <= 0x3f)
+ return achar;
+ else
+ return 0x00;
+}
+
+MidiByteArray
+Surface::two_char_display (const std::string & msg, const std::string & dots)
+{
+ if (msg.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: msg must be exactly 2 characters");
+ if (dots.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: dots must be exactly 2 characters");
+
+ MidiByteArray bytes (6, 0xb0, 0x4a, 0x00, 0xb0, 0x4b, 0x00);
+
+ // chars are understood by the surface in right-to-left order
+ // could also exchange the 0x4a and 0x4b, above
+ bytes[5] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
+ bytes[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
+
+ return bytes;
+}
+
+MidiByteArray
+Surface::two_char_display (unsigned int value, const std::string & /*dots*/)
+{
+ ostringstream os;
+ os << setfill('0') << setw(2) << value % 100;
+ return two_char_display (os.str());
+}
+
+void
+Surface::display_timecode (const std::string & timecode, const std::string & timecode_last)
+{
+ if (has_timecode_display()) {
+ _port->write (timecode_display (timecode, timecode_last));
+ }
+}
+
+MidiByteArray
+Surface::timecode_display (const std::string & timecode, const std::string & last_timecode)
+{
+ // if there's no change, send nothing, not even sysex header
+ if (timecode == last_timecode) return MidiByteArray();
+
+ // length sanity checking
+ string local_timecode = timecode;
+
+ // truncate to 10 characters
+ if (local_timecode.length() > 10) {
+ local_timecode = local_timecode.substr (0, 10);
+ }
+
+ // pad to 10 characters
+ while (local_timecode.length() < 10) {
+ local_timecode += " ";
+ }
+
+ // find the suffix of local_timecode that differs from last_timecode
+ std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
+
+ MidiByteArray retval;
+
+ // sysex header
+ retval << sysex_hdr();
+
+ // code for timecode display
+ retval << 0x10;
+
+ // translate characters. These are sent in reverse order of display
+ // hence the reverse iterators
+ string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
+ for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
+ retval << translate_seven_segment (*it);
+ }
+
+ // sysex trailer
+ retval << MIDI::eox;
+
+ return retval;
+}
+
#include "controls.h"
#include "types.h"
-#include "mackie_midi_builder.h"
#include "mackie_jog_wheel.h"
namespace MIDI {
class Jog;
class Pot;
class Led;
-class LedRing;
class Surface : public PBD::ScopedConnectionList
{
void handle_control_event (Mackie::Control & control, const Mackie::ControlState & state);
+ // 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 = " ");
+
+ /**
+ 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 (const std::string & timecode, const std::string & last_timecode = "");
+
protected:
void init_controls();
void init_strips ();
bool _active;
bool _connected;
Mackie::JogWheel* _jog_wheel;
- MackieMidiBuilder builder;
void jog_wheel_state_display (Mackie::JogWheel::State state);
};
class Group;
class Pot;
class Led;
-class LedRing;
}
led.cc
mackie_control_protocol.cc
mackie_jog_wheel.cc
- mackie_midi_builder.cc
mcp_buttons.cc
meter.cc
midi_byte_array.cc
+ pot.cc
strip.cc
surface.cc
surface_port.cc