2 Copyright (C) 2012 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/convert.h>
28 #include "midi++/port.h"
30 #include "ardour/audioengine.h"
31 #include "ardour/automation_control.h"
32 #include "ardour/debug.h"
33 #include "ardour/route.h"
34 #include "ardour/panner.h"
35 #include "ardour/panner_shell.h"
36 #include "ardour/rc_configuration.h"
37 #include "ardour/session.h"
38 #include "ardour/utils.h"
40 #include "control_group.h"
41 #include "surface_port.h"
44 #include "mackie_control_protocol.h"
45 #include "jog_wheel.h"
57 #ifdef PLATFORM_WINDOWS
58 #define random() rand()
65 using ARDOUR::Pannable;
66 using ARDOUR::AutomationControl;
67 using namespace ArdourSurface;
68 using namespace Mackie;
70 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
72 // The MCU sysex header.4th byte Will be overwritten
73 // when we get an incoming sysex that identifies
75 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
77 // The MCU extender sysex header.4th byte Will be overwritten
78 // when we get an incoming sysex that identifies
80 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
82 static MidiByteArray empty_midi_byte_array;
84 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
93 , _last_master_gain_written (-0.0f)
94 , connection_state (0)
96 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
99 _port = new SurfacePort (*this);
101 throw failed_constructor ();
104 /* only the first Surface object has global controls */
105 /* lets use master_position instead */
106 uint32_t mp = _mcp.device_info().master_position();
108 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
109 if (_mcp.device_info().has_global_controls()) {
111 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
114 if (_mcp.device_info().has_master_fader()) {
116 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
120 uint32_t n = _mcp.device_info().strip_cnt();
124 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
130 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Surface::connection_handler, this, _1, _2, _3, _4, _5), &_mcp);
132 connect_to_signals ();
134 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
139 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
144 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
149 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
153 port_connection.disconnect ();
158 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
162 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
168 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
169 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
171 if (ni == name1 || ni == name2) {
173 connection_state |= InputConnected;
175 connection_state &= ~InputConnected;
177 } else if (no == name1 || no == name2) {
179 connection_state |= OutputConnected;
181 connection_state &= ~OutputConnected;
185 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
187 /* this will send a device query message, which should
188 result in a response that will kick off device type
189 discovery and activation of the surface(s).
191 The intended order of events is:
193 - each surface sends a device query message
194 - devices respond with either MCP or LCP response (sysex in both
196 - sysex message causes Surface::turn_it_on() which tells the
197 MCP object that the surface is ready, and sets up strip
198 displays and binds faders and buttons for that surface
200 In the case of LCP, where this is a handshake process that could
201 fail, the response process to the initial sysex after a device query
202 will mark the surface inactive, which won't shut anything down
203 but will stop any writes to the device.
205 Note: there are no known cases of the handshake process failing.
207 We actually can't initiate this in this callback, so we have
208 to queue it with the MCP event loop.
214 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
223 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
224 XMLNode* node = new XMLNode (buf);
226 node->add_child_nocopy (_port->get_state());
232 Surface::set_state (const XMLNode& node, int version)
235 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
236 XMLNode* mynode = node.child (buf);
242 XMLNode* portnode = mynode->child (X_("Port"));
244 if (_port->set_state (*portnode, version)) {
253 Surface::sysex_hdr() const
256 case mcu: return mackie_sysex_hdr;
257 case ext: return mackie_sysex_hdr_xt;
259 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
260 return mackie_sysex_hdr;
263 static GlobalControlDefinition mackie_global_controls[] = {
264 { "external", Pot::External, Pot::factory, "none" },
265 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
266 { "timecode", Led::Timecode, Led::factory, "none" },
267 { "beats", Led::Beats, Led::factory, "none" },
268 { "solo", Led::RudeSolo, Led::factory, "none" },
269 { "relay_click", Led::RelayClick, Led::factory, "none" },
270 { "", 0, Led::factory, "" }
274 Surface::init_controls()
278 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
279 groups["assignment"] = new Group ("assignment");
280 groups["automation"] = new Group ("automation");
281 groups["bank"] = new Group ("bank");
282 groups["cursor"] = new Group ("cursor");
283 groups["display"] = new Group ("display");
284 groups["function select"] = new Group ("function select");
285 groups["global view"] = new Group ("global view");
286 groups["master"] = new Group ("master");
287 groups["modifiers"] = new Group ("modifiers");
288 groups["none"] = new Group ("none");
289 groups["transport"] = new Group ("transport");
290 groups["user"] = new Group ("user");
291 groups["utilities"] = new Group ("utilities");
293 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
294 if (_mcp.device_info().has_jog_wheel()) {
295 _jog_wheel = new Mackie::JogWheel (_mcp);
298 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
299 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
300 group = groups[mackie_global_controls[n].group_name];
301 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
302 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
305 /* add global buttons */
306 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
307 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
309 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
310 group = groups[b->second.group];
311 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
316 Surface::init_strips (uint32_t n)
318 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
320 for (uint32_t i = 0; i < n; ++i) {
324 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
326 Strip* strip = new Strip (*this, name, i, strip_buttons);
328 groups[name] = strip;
329 strips.push_back (strip);
334 Surface::setup_master ()
336 boost::shared_ptr<Route> m;
338 if ((m = _mcp.get_session().monitor_out()) == 0) {
339 m = _mcp.get_session().master_out();
346 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
348 _master_fader->set_control (m->gain_control());
349 m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
351 Groups::iterator group_it;
352 group_it = groups.find("master");
354 DeviceInfo device_info = _mcp.device_info();
355 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
356 Button* bb = dynamic_cast<Button*> (Button::factory (
358 Button::MasterFaderTouch,
363 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
364 number(), Button::MasterFaderTouch, bb->id()));
368 Surface::master_gain_changed ()
370 if (!_master_fader) {
374 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
379 float normalized_position = ac->internal_to_interface (ac->get_value());
380 if (normalized_position == _last_master_gain_written) {
384 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
386 _port->write (_master_fader->set_position (normalized_position));
387 _last_master_gain_written = normalized_position;
391 Surface::scaled_delta (float delta, float current_speed)
393 /* XXX needs work before use */
394 const float sign = delta < 0.0 ? -1.0 : 1.0;
396 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
400 Surface::display_bank_start (uint32_t current_bank)
402 if (current_bank == 0) {
403 // send Ar. to 2-char display on the master
404 show_two_char_display ("Ar", "..");
406 // write the current first remote_id to the 2-char display
407 show_two_char_display (current_bank);
412 Surface::blank_jog_ring ()
414 Control* control = controls_by_device_independent_id[Jog::ID];
417 Pot* pot = dynamic_cast<Pot*> (control);
419 _port->write (pot->set (0.0, false, Pot::spread));
425 Surface::scrub_scaling_factor () const
431 Surface::connect_to_signals ()
436 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
437 number(), _port->input_port().name()));
439 MIDI::Parser* p = _port->input_port().parser();
442 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
443 /* V-Pot messages are Controller */
444 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
445 /* Button messages are NoteOn */
446 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
447 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
448 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
449 /* Fader messages are Pitchbend */
451 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
452 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
455 p->channel_pitchbend[_mcp.device_info().strip_cnt()].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, _mcp.device_info().strip_cnt()));
462 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
464 /* Pitchbend messages are fader position messages. Nothing in the data we get
465 * from the MIDI::Parser conveys the fader ID, which was given by the
466 * channel ID in the status byte.
468 * Instead, we have used bind() to supply the fader-within-strip ID
469 * when we connected to the per-channel pitchbend events.
472 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
473 fader_id, pb, _number, pb/16384.0));
475 if (_mcp.device_info().no_handshake()) {
479 if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
480 /* user is doing a reset to unity gain but device sends a PB
481 * message in the middle of the touch on/off messages. Ignore
487 Fader* fader = faders[fader_id];
490 Strip* strip = dynamic_cast<Strip*> (&fader->group());
491 float pos = pb / 16384.0;
493 strip->handle_fader (*fader, pos);
495 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
497 fader->set_value (pos); // alter master gain
498 _port->write (fader->set_position (pos)); // write back value (required for servo)
501 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
506 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
508 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
510 if (_mcp.device_info().no_handshake()) {
514 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
518 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
522 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
523 Fader* fader = faders[ev->note_number];
525 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
529 Strip* strip = dynamic_cast<Strip*> (&fader->group());
531 if (ev->velocity > 64) {
532 strip->handle_fader_touch (*fader, true);
534 strip->handle_fader_touch (*fader, false);
540 Button* button = buttons[ev->note_number];
544 if (ev->velocity > 64) {
548 Strip* strip = dynamic_cast<Strip*> (&button->group());
551 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
552 strip->index(), button->name(), (ev->velocity > 64)));
553 strip->handle_button (*button, ev->velocity > 64 ? press : release);
556 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
557 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
560 if (ev->velocity <= 64) {
565 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
568 /* button release should reset timer AFTER handler(s) have run */
572 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
574 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
576 if (_mcp.device_info().no_handshake()) {
580 Pot* pot = pots[ev->controller_number];
582 // bit 6 gives the sign
583 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
584 // bits 0..5 give the velocity. we interpret this as "ticks
585 // moved before this message was sent"
586 float ticks = (ev->value & 0x3f);
588 /* euphonix and perhaps other devices send zero
589 when they mean 1, we think.
595 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_CONTROL) {
596 delta = sign * (ticks / (float) 0xff);
598 delta = sign * (ticks / (float) 0x3f);
602 if (ev->controller_number == Jog::ID && _jog_wheel) {
604 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
605 _jog_wheel->jog_event (delta);
608 // add external (pedal?) control here
613 Strip* strip = dynamic_cast<Strip*> (&pot->group());
615 strip->handle_pot (*pot, delta);
620 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
622 MidiByteArray bytes (count, raw_bytes);
624 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
626 if (_mcp.device_info().no_handshake()) {
630 /* always save the device type ID so that our outgoing sysex messages
635 mackie_sysex_hdr[4] = bytes[4];
637 mackie_sysex_hdr_xt[4] = bytes[4];
643 LCP: Connection Challenge
645 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
646 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
647 write_sysex (host_connection_query (bytes));
649 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
656 case 0x03: /* LCP Connection Confirmation */
657 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
658 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
659 write_sysex (host_connection_confirmation (bytes));
664 case 0x04: /* LCP: Confirmation Denied */
665 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
669 error << "MCP: unknown sysex: " << bytes << endmsg;
674 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
677 back_insert_iterator<MidiByteArray> back (l);
678 copy (begin, end, back);
680 MidiByteArray retval;
682 // this is how to calculate the response to the challenge.
683 // from the Logic docs.
684 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
685 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
686 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
687 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
693 Surface::host_connection_query (MidiByteArray & bytes)
695 MidiByteArray response;
697 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
698 /* not a Logic Control device - no response required */
702 // handle host connection query
703 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
705 if (bytes.size() != 18) {
706 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
710 // build and send host connection reply
712 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
713 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
718 Surface::host_connection_confirmation (const MidiByteArray & bytes)
720 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
722 // decode host connection confirmation
723 if (bytes.size() != 14) {
725 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
726 throw MackieControlException (os.str());
729 // send version request
730 return MidiByteArray (2, 0x13, 0x00);
734 Surface::turn_it_on ()
742 _mcp.device_ready ();
744 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
748 update_view_mode_display ();
750 if (_mcp.device_info ().has_global_controls ()) {
751 _mcp.update_global_button (Button::Read, _mcp.metering_active ());
756 Surface::write_sysex (const MidiByteArray & mba)
763 buf << sysex_hdr() << mba << MIDI::eox;
768 Surface::write_sysex (MIDI::byte msg)
771 buf << sysex_hdr() << msg << MIDI::eox;
776 Surface::n_strips (bool with_locked_strips) const
778 if (with_locked_strips) {
779 return strips.size();
784 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
785 if (!(*it)->locked()) {
793 Surface::nth_strip (uint32_t n) const
795 if (n > n_strips()) {
804 if (_mcp.device_info().has_timecode_display ()) {
805 display_timecode (string (10, '0'), string (10, ' '));
808 if (_mcp.device_info().has_two_character_display()) {
809 show_two_char_display (string (2, '0'), string (2, ' '));
812 if (_mcp.device_info().has_master_fader () && _master_fader) {
813 _port->write (_master_fader->zero ());
817 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
825 Surface::zero_controls ()
827 if (!_mcp.device_info().has_global_controls()) {
831 // turn off global buttons and leds
833 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
834 Control & control = **it;
835 if (!control.group().is_strip()) {
836 _port->write (control.zero());
840 // and the led ring for the master strip
843 _last_master_gain_written = 0.0f;
847 Surface::periodic (uint64_t now_usecs)
849 master_gain_changed();
850 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
851 (*s)->periodic (now_usecs);
856 Surface::redisplay (ARDOUR::microseconds_t now)
858 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
859 (*s)->redisplay (now);
864 Surface::write (const MidiByteArray& data)
869 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
874 Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
876 vector<boost::shared_ptr<Route> >::const_iterator r;
877 Strips::iterator s = strips.begin();
879 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes\n", routes.size()));
881 for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
883 /* don't try to assign routes to a locked strip. it won't
884 use it anyway, but if we do, then we get out of sync
885 with the proposed mapping.
888 if (!(*s)->locked()) {
889 (*s)->set_route (*r);
894 for (; s != strips.end(); ++s) {
895 (*s)->set_route (boost::shared_ptr<Route>());
902 translate_seven_segment (char achar)
904 achar = toupper (achar);
906 if (achar >= 0x40 && achar <= 0x60) {
908 } else if (achar >= 0x21 && achar <= 0x3f) {
916 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
918 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
922 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
923 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
925 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
926 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
928 _port->write (right);
933 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
936 os << setfill('0') << setw(2) << value % 100;
937 show_two_char_display (os.str());
941 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
943 if (!_active || !_mcp.device_info().has_timecode_display()) {
946 // if there's no change, send nothing, not even sysex header
947 if (timecode == last_timecode) return;
949 // length sanity checking
950 string local_timecode = timecode;
952 // truncate to 10 characters
953 if (local_timecode.length() > 10) {
954 local_timecode = local_timecode.substr (0, 10);
957 // pad to 10 characters
958 while (local_timecode.length() < 10) {
959 local_timecode += " ";
962 // translate characters.
963 // Only the characters that actually changed are sent.
966 for (i = local_timecode.length () - 1; i >= 0; i--) {
968 if (local_timecode[i] == last_timecode[i]) {
971 MidiByteArray retval (2, 0xb0, position);
972 retval << translate_seven_segment (local_timecode[i]);
973 _port->write (retval);
978 Surface::update_flip_mode_display ()
980 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
981 (*s)->flip_mode_changed (true);
986 Surface::update_potmode ()
988 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
989 (*s)->potmode_changed (false);
994 Surface::update_view_mode_display ()
1003 switch (_mcp.view_mode()) {
1004 case MackieControlProtocol::Mixer:
1005 show_two_char_display ("Mx");
1008 case MackieControlProtocol::Loop:
1009 show_two_char_display ("LP");
1012 case MackieControlProtocol::AudioTracks:
1013 show_two_char_display ("AT");
1015 case MackieControlProtocol::MidiTracks:
1016 show_two_char_display ("MT");
1024 /* we are attempting to turn a global button/LED on */
1026 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1028 if (x != controls_by_device_independent_id.end()) {
1029 Button* button = dynamic_cast<Button*> (x->second);
1031 _port->write (button->set_state (on));
1036 if (!text.empty()) {
1037 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1038 _port->write ((*s)->display (1, text));
1044 Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
1046 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1047 (*s)->gui_selection_changed (routes);
1052 Surface::say_hello ()
1054 /* wakeup for Mackie Control */
1055 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1056 _port->write (wakeup);
1057 wakeup[4] = 0x15; /* wakup Mackie XT */
1058 _port->write (wakeup);
1059 wakeup[4] = 0x10; /* wakeup Logic Control */
1060 _port->write (wakeup);
1061 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1062 _port->write (wakeup);
1066 Surface::next_jog_mode ()
1071 Surface::set_jog_mode (JogWheel::Mode)
1076 Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
1078 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1079 if ((*s)->route() == r && (*s)->locked()) {
1087 Surface::notify_metering_state_changed()
1089 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1090 (*s)->notify_metering_state_changed ();
1098 /* reset msg for Mackie Control */
1109 Surface::toggle_backlight ()
1112 int onoff = random() %2;
1114 msg << sysex_hdr ();
1116 msg << (onoff ? 0x1 : 0x0);
1123 Surface::recalibrate_faders ()
1127 msg << sysex_hdr ();
1136 Surface::set_touch_sensitivity (int sensitivity)
1138 /* NOTE: assumed called from GUI code, hence sleep() */
1140 /* sensitivity already clamped by caller */
1145 msg << sysex_hdr ();
1147 msg << 0xff; /* overwritten for each fader below */
1148 msg << (sensitivity & 0x7f);
1151 for (int fader = 0; fader < 9; ++fader) {
1159 Surface::hui_heartbeat ()
1165 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1170 Surface::connected ()
1172 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1176 if (_mcp.device_info().no_handshake()) {
1182 Surface::display_line (string const& msg, int line_num)
1184 MidiByteArray midi_msg;
1185 midi_msg << sysex_hdr ();
1187 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1189 * correspond to line
1194 midi_msg.insert (midi_msg.end(), 55, ' ');
1198 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1199 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1200 string::size_type len = ascii.length();
1203 midi_msg << ascii.substr (0, 55);
1207 for (string::size_type i = len; i < 55; ++i) {
1213 midi_msg << MIDI::eox;
1218 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1220 * @param msg is assumed to be UTF-8 encoded, and will be converted
1221 * to ASCII with an underscore as fallback character before being
1222 * sent to the device.
1225 Surface::display_message_for (string const& msg, uint64_t msecs)
1227 string::size_type newline;
1229 if ((newline = msg.find ('\n')) == string::npos) {
1231 _port->write (display_line (msg, 0));
1232 _port->write (display_line (string(), 1));
1234 } else if (newline == 0) {
1236 _port->write (display_line (string(), 0));
1237 _port->write (display_line (msg.substr (1), 1));
1241 string first_line = msg.substr (0, newline-1);
1242 string second_line = msg.substr (newline+1);
1244 _port->write (display_line (first_line, 0));
1245 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1248 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1249 (*s)->block_screen_display_for (msecs);