diff --git a/libs/surfaces/mackie/button.h b/libs/surfaces/mackie/button.h index 4e624aba75..e6712234c0 100644 --- a/libs/surfaces/mackie/button.h +++ b/libs/surfaces/mackie/button.h @@ -102,9 +102,9 @@ public: , _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&); diff --git a/libs/surfaces/mackie/controls.cc b/libs/surfaces/mackie/controls.cc index 972a28cb0d..6c9fb559f4 100644 --- a/libs/surfaces/mackie/controls.cc +++ b/libs/surfaces/mackie/controls.cc @@ -22,13 +22,11 @@ #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" @@ -75,10 +73,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control) 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(); @@ -87,16 +81,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control) 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) { diff --git a/libs/surfaces/mackie/controls.h b/libs/surfaces/mackie/controls.h index 9d5163680f..f946f1507d 100644 --- a/libs/surfaces/mackie/controls.h +++ b/libs/surfaces/mackie/controls.h @@ -28,30 +28,21 @@ #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, @@ -65,12 +56,6 @@ public: 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. @@ -78,19 +63,17 @@ public: 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. */ diff --git a/libs/surfaces/mackie/fader.cc b/libs/surfaces/mackie/fader.cc index 80928d85eb..9b4100842f 100644 --- a/libs/surfaces/mackie/fader.cc +++ b/libs/surfaces/mackie/fader.cc @@ -34,3 +34,16 @@ Fader::factory (Surface& surface, int id, const char* name, Group& group) 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); +} diff --git a/libs/surfaces/mackie/fader.h b/libs/surfaces/mackie/fader.h index 7c64016826..3520699e98 100644 --- a/libs/surfaces/mackie/fader.h +++ b/libs/surfaces/mackie/fader.h @@ -10,12 +10,19 @@ class Fader : public Control 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; }; } diff --git a/libs/surfaces/mackie/jog.h b/libs/surfaces/mackie/jog.h index c769319766..abccd01b06 100644 --- a/libs/surfaces/mackie/jog.h +++ b/libs/surfaces/mackie/jog.h @@ -33,7 +33,7 @@ public: { } - virtual bool is_jog() const { return true; } + MidiByteArray zero() { return MidiByteArray(); } static Control* factory (Surface&, int id, const char*, Group&); }; diff --git a/libs/surfaces/mackie/led.h b/libs/surfaces/mackie/led.h index 47ea2adfa0..fca9b78a43 100644 --- a/libs/surfaces/mackie/led.h +++ b/libs/surfaces/mackie/led.h @@ -36,8 +36,9 @@ public: } 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&); diff --git a/libs/surfaces/mackie/ledring.h b/libs/surfaces/mackie/ledring.h deleted file mode 100644 index 284c2d26a3..0000000000 --- a/libs/surfaces/mackie/ledring.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - 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 diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index cd8eb5a8c8..43c30d2ac8 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -55,7 +55,6 @@ #include "midi_byte_array.h" #include "mackie_control_exception.h" -#include "mackie_midi_builder.h" #include "surface_port.h" #include "surface.h" @@ -365,8 +364,6 @@ MackieControlProtocol::periodic () (*s)->periodic (); } - update_timecode_display(); - return true; } diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h index ab971e1b78..a4b2c0adb2 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.h +++ b/libs/surfaces/mackie/mackie_control_protocol.h @@ -33,12 +33,12 @@ #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 { @@ -371,8 +371,6 @@ class MackieControlProtocol int _modifier_state; - Mackie::MackieMidiBuilder builder; - typedef std::list PortSources; PortSources port_sources; diff --git a/libs/surfaces/mackie/mackie_jog_wheel.cc b/libs/surfaces/mackie/mackie_jog_wheel.cc index 8439c68fe7..917dd426e0 100644 --- a/libs/surfaces/mackie/mackie_jog_wheel.cc +++ b/libs/surfaces/mackie/mackie_jog_wheel.cc @@ -3,7 +3,6 @@ #include "ardour/session.h" #include "mackie_jog_wheel.h" - #include "mackie_control_protocol.h" #include "surface_port.h" #include "controls.h" diff --git a/libs/surfaces/mackie/mackie_midi_builder.cc b/libs/surfaces/mackie/mackie_midi_builder.cc deleted file mode 100644 index bfc6ea50cf..0000000000 --- a/libs/surfaces/mackie/mackie_midi_builder.cc +++ /dev/null @@ -1,276 +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 "mackie_midi_builder.h" - -#include -#include -#include -#include -#include - -#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(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 (control), off); - - case Control::type_led_ring: - return build_led_ring (dynamic_cast (control), off); - - case Control::type_meter: - return const_cast(dynamic_cast(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 & /*lines1*/, std::vector & /*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 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 (pp.second); - for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) { - retval << translate_seven_segment (*it); - } - - // sysex trailer - retval << MIDI::eox; - - return retval; -} diff --git a/libs/surfaces/mackie/mackie_midi_builder.h b/libs/surfaces/mackie/mackie_midi_builder.h deleted file mode 100644 index 8836f3de2e..0000000000 --- a/libs/surfaces/mackie/mackie_midi_builder.h +++ /dev/null @@ -1,111 +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_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 & lines1, std::vector & lines2); - -protected: - static MIDI::byte calculate_pot_value (midi_pot_mode mode, const ControlState &); -}; - -} - -#endif diff --git a/libs/surfaces/mackie/meter.cc b/libs/surfaces/mackie/meter.cc index 5bc7c49096..03c0f1aa30 100644 --- a/libs/surfaces/mackie/meter.cc +++ b/libs/surfaces/mackie/meter.cc @@ -95,3 +95,4 @@ Meter::update_message (float dB) return msg; } + diff --git a/libs/surfaces/mackie/meter.h b/libs/surfaces/mackie/meter.h index 7110f04416..1898bf8199 100644 --- a/libs/surfaces/mackie/meter.h +++ b/libs/surfaces/mackie/meter.h @@ -35,10 +35,10 @@ public: , 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; diff --git a/libs/surfaces/mackie/pot.cc b/libs/surfaces/mackie/pot.cc new file mode 100644 index 0000000000..67fcb65ef9 --- /dev/null +++ b/libs/surfaces/mackie/pot.cc @@ -0,0 +1,87 @@ +/* + 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 + +#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); + +} + diff --git a/libs/surfaces/mackie/pot.h b/libs/surfaces/mackie/pot.h index 767ea6e6df..fc9858e24c 100644 --- a/libs/surfaces/mackie/pot.h +++ b/libs/surfaces/mackie/pot.h @@ -2,25 +2,40 @@ #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; }; } diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc index 610fe8dd3b..e75adf72ae 100644 --- a/libs/surfaces/mackie/strip.cc +++ b/libs/surfaces/mackie/strip.cc @@ -40,7 +40,6 @@ #include "surface.h" #include "button.h" #include "led.h" -#include "ledring.h" #include "pot.h" #include "fader.h" #include "jog.h" @@ -110,12 +109,8 @@ void Strip::add (Control & control) _fader_touch = reinterpret_cast(&control); } else if (control.name() == "meter") { _meter = reinterpret_cast(&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 } } @@ -308,27 +303,24 @@ Strip::notify_all() 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)); } } @@ -350,19 +342,12 @@ 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 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; } } } @@ -385,8 +370,8 @@ Strip::notify_property_changed (const PropertyChange& what_changed) 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)); } } @@ -396,13 +381,16 @@ Strip::notify_panner_changed (bool force_update) if (_route) { Pot & pot = vpot(); boost::shared_ptr 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) { @@ -410,7 +398,7 @@ Strip::notify_panner_changed (bool force_update) _last_pan_written = bytes; } } else { - _surface->write (builder.zero_control (pot)); + _surface->write (pot.zero()); } } } @@ -418,10 +406,16 @@ Strip::notify_panner_changed (bool force_update) bool Strip::handle_button (SurfacePort & port, Control & control, ButtonState bs) { + Button* button = dynamic_cast(&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; } @@ -500,3 +494,61 @@ Strip::update_meter () float dB = const_cast (_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; +} diff --git a/libs/surfaces/mackie/strip.h b/libs/surfaces/mackie/strip.h index 97997e7f0b..cfca9c166e 100644 --- a/libs/surfaces/mackie/strip.h +++ b/libs/surfaces/mackie/strip.h @@ -5,9 +5,11 @@ #include #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; @@ -21,6 +23,7 @@ class Button; class Pot; class Fader; class Meter; +class SurfacePort; struct StripControlDefinition { const char* name; @@ -78,6 +81,10 @@ public: 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; @@ -91,8 +98,6 @@ private: int _index; Surface* _surface; - MackieMidiBuilder builder; - boost::shared_ptr _route; PBD::ScopedConnectionList route_connections; diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 2fb442128c..ebe626d425 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -17,14 +17,12 @@ #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" @@ -260,14 +258,6 @@ Surface::init_strips () } } -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) { @@ -279,10 +269,10 @@ 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", "..")); + _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)); } } @@ -292,7 +282,10 @@ Surface::blank_jog_ring () Control* control = controls_by_name["jog"]; if (control) { - _port->write (builder.build_led_ring (*(dynamic_cast (control)), off)); + Pot* pot = dynamic_cast (control); + if (pot) { + _port->write (pot->set_onoff (false)); + } } } @@ -423,8 +416,11 @@ Surface::handle_control_event (Control & control, const ControlState & state) // 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 (&control); + Button* button = dynamic_cast (&control); + Pot* pot = dynamic_cast (&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 @@ -441,25 +437,25 @@ Surface::handle_control_event (Control & control, const ControlState & state) // 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(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 = route->panner_shell()->panner(); // pan for mono input routes, or stereo linked panners @@ -474,23 +470,21 @@ Surface::handle_control_event (Control & control, const ControlState & state) } } else { // it's a pot for an umnapped route, so turn all the lights off - _port->write (builder.build_led_ring (dynamic_cast (control), off)); + Pot* pot = dynamic_cast (&control); + if (pot) { + _port->write (pot->set_onoff (false)); + } } } else { - if (control.is_jog()) { + JogWheel* wheel = dynamic_cast (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; } } @@ -550,7 +544,7 @@ Surface::zero_all () // 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 @@ -560,13 +554,13 @@ Surface::zero_all () 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 (); @@ -578,6 +572,7 @@ Surface::periodic () for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) { (*s)->periodic (); } + } void @@ -591,22 +586,22 @@ Surface::jog_wheel_state_display (JogWheel::State state) { 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; } } @@ -625,3 +620,90 @@ Surface::map_routes (const vector >& routes) (*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 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 (pp.second); + for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) { + retval << translate_seven_segment (*it); + } + + // sysex trailer + retval << MIDI::eox; + + return retval; +} + diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 52ac8f14ba..dbc4f2530b 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -7,7 +7,6 @@ #include "controls.h" #include "types.h" -#include "mackie_midi_builder.h" #include "mackie_jog_wheel.h" namespace MIDI { @@ -33,7 +32,6 @@ class Fader; class Jog; class Pot; class Led; -class LedRing; class Surface : public PBD::ScopedConnectionList { @@ -132,6 +130,19 @@ public: 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 (); @@ -144,7 +155,6 @@ public: bool _active; bool _connected; Mackie::JogWheel* _jog_wheel; - MackieMidiBuilder builder; void jog_wheel_state_display (Mackie::JogWheel::State state); }; diff --git a/libs/surfaces/mackie/types.h b/libs/surfaces/mackie/types.h index bdddf0b248..d906af6d32 100644 --- a/libs/surfaces/mackie/types.h +++ b/libs/surfaces/mackie/types.h @@ -106,7 +106,6 @@ class Strip; class Group; class Pot; class Led; -class LedRing; } diff --git a/libs/surfaces/mackie/wscript b/libs/surfaces/mackie/wscript index 6c0a719654..b7712865d5 100644 --- a/libs/surfaces/mackie/wscript +++ b/libs/surfaces/mackie/wscript @@ -29,10 +29,10 @@ def build(bld): 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