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)
97 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
100 _port = new SurfacePort (*this);
102 throw failed_constructor ();
105 /* only the first Surface object has global controls */
106 /* lets use master_position instead */
107 uint32_t mp = _mcp.device_info().master_position();
109 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
110 if (_mcp.device_info().has_global_controls()) {
112 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
115 if (_mcp.device_info().has_master_fader()) {
117 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
121 uint32_t n = _mcp.device_info().strip_cnt();
125 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
128 if (!_mcp.device_info().uses_ipmidi()) {
129 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Surface::connection_handler, this, _1, _2, _3, _4, _5), &_mcp);
131 /* ipMIDI port already exists, we can just assume that we're
134 * If the user still hasn't connected the ipMIDI surface and/or
135 * turned it on, then they have to press "Discover Mackie
136 * Devices" in the GUI at the right time.
139 connection_state |= (InputConnected|OutputConnected);
143 connect_to_signals ();
145 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
150 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
152 port_connection.disconnect ();
155 g_source_destroy (input_source);
159 // delete groups (strips)
160 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
164 // delete controls (global buttons, master fader etc)
165 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
172 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
176 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
182 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
183 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
185 if (ni == name1 || ni == name2) {
187 connection_state |= InputConnected;
189 connection_state &= ~InputConnected;
191 } else if (no == name1 || no == name2) {
193 connection_state |= OutputConnected;
195 connection_state &= ~OutputConnected;
199 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
201 /* this will send a device query message, which should
202 result in a response that will kick off device type
203 discovery and activation of the surface(s).
205 The intended order of events is:
207 - each surface sends a device query message
208 - devices respond with either MCP or LCP response (sysex in both
210 - sysex message causes Surface::turn_it_on() which tells the
211 MCP object that the surface is ready, and sets up strip
212 displays and binds faders and buttons for that surface
214 In the case of LCP, where this is a handshake process that could
215 fail, the response process to the initial sysex after a device query
216 will mark the surface inactive, which won't shut anything down
217 but will stop any writes to the device.
219 Note: there are no known cases of the handshake process failing.
221 We actually can't initiate this in this callback, so we have
222 to queue it with the MCP event loop.
228 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
237 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
238 XMLNode* node = new XMLNode (buf);
240 node->add_child_nocopy (_port->get_state());
246 Surface::set_state (const XMLNode& node, int version)
249 snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
250 XMLNode* mynode = node.child (buf);
256 XMLNode* portnode = mynode->child (X_("Port"));
258 if (_port->set_state (*portnode, version)) {
267 Surface::sysex_hdr() const
270 case mcu: return mackie_sysex_hdr;
271 case ext: return mackie_sysex_hdr_xt;
273 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
274 return mackie_sysex_hdr;
277 static GlobalControlDefinition mackie_global_controls[] = {
278 { "external", Pot::External, Pot::factory, "none" },
279 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
280 { "timecode", Led::Timecode, Led::factory, "none" },
281 { "beats", Led::Beats, Led::factory, "none" },
282 { "solo", Led::RudeSolo, Led::factory, "none" },
283 { "relay_click", Led::RelayClick, Led::factory, "none" },
284 { "", 0, Led::factory, "" }
288 Surface::init_controls()
292 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
293 groups["assignment"] = new Group ("assignment");
294 groups["automation"] = new Group ("automation");
295 groups["bank"] = new Group ("bank");
296 groups["cursor"] = new Group ("cursor");
297 groups["display"] = new Group ("display");
298 groups["function select"] = new Group ("function select");
299 groups["global view"] = new Group ("global view");
300 groups["master"] = new Group ("master");
301 groups["modifiers"] = new Group ("modifiers");
302 groups["none"] = new Group ("none");
303 groups["transport"] = new Group ("transport");
304 groups["user"] = new Group ("user");
305 groups["utilities"] = new Group ("utilities");
307 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
308 if (_mcp.device_info().has_jog_wheel()) {
309 _jog_wheel = new Mackie::JogWheel (_mcp);
312 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
313 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
314 group = groups[mackie_global_controls[n].group_name];
315 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
316 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
319 /* add global buttons */
320 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
321 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
323 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
324 group = groups[b->second.group];
325 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
330 Surface::init_strips (uint32_t n)
332 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
334 for (uint32_t i = 0; i < n; ++i) {
338 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
340 Strip* strip = new Strip (*this, name, i, strip_buttons);
342 groups[name] = strip;
343 strips.push_back (strip);
348 Surface::setup_master ()
350 boost::shared_ptr<Route> m;
352 if ((m = _mcp.get_session().monitor_out()) == 0) {
353 m = _mcp.get_session().master_out();
360 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
362 _master_fader->set_control (m->gain_control());
363 m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
365 Groups::iterator group_it;
366 group_it = groups.find("master");
368 DeviceInfo device_info = _mcp.device_info();
369 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
370 Button* bb = dynamic_cast<Button*> (Button::factory (
372 Button::MasterFaderTouch,
377 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
378 number(), Button::MasterFaderTouch, bb->id()));
382 Surface::master_gain_changed ()
384 if (!_master_fader) {
388 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
393 float normalized_position = ac->internal_to_interface (ac->get_value());
394 if (normalized_position == _last_master_gain_written) {
398 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
400 _port->write (_master_fader->set_position (normalized_position));
401 _last_master_gain_written = normalized_position;
405 Surface::scaled_delta (float delta, float current_speed)
407 /* XXX needs work before use */
408 const float sign = delta < 0.0 ? -1.0 : 1.0;
410 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
414 Surface::display_bank_start (uint32_t current_bank)
416 if (current_bank == 0) {
417 // send Ar. to 2-char display on the master
418 show_two_char_display ("Ar", "..");
420 // write the current first remote_id to the 2-char display
421 show_two_char_display (current_bank);
426 Surface::blank_jog_ring ()
428 Control* control = controls_by_device_independent_id[Jog::ID];
431 Pot* pot = dynamic_cast<Pot*> (control);
433 _port->write (pot->set (0.0, false, Pot::spread));
439 Surface::scrub_scaling_factor () const
445 Surface::connect_to_signals ()
450 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
451 number(), _port->input_port().name()));
453 MIDI::Parser* p = _port->input_port().parser();
456 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
457 /* V-Pot messages are Controller */
458 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
459 /* Button messages are NoteOn */
460 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
461 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
462 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
463 /* Fader messages are Pitchbend */
465 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
466 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
469 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()));
476 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
478 /* Pitchbend messages are fader position messages. Nothing in the data we get
479 * from the MIDI::Parser conveys the fader ID, which was given by the
480 * channel ID in the status byte.
482 * Instead, we have used bind() to supply the fader-within-strip ID
483 * when we connected to the per-channel pitchbend events.
486 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
487 fader_id, pb, _number, pb/16384.0));
489 if (_mcp.device_info().no_handshake()) {
493 if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
494 /* user is doing a reset to unity gain but device sends a PB
495 * message in the middle of the touch on/off messages. Ignore
501 Fader* fader = faders[fader_id];
504 Strip* strip = dynamic_cast<Strip*> (&fader->group());
505 float pos = pb / 16384.0;
507 strip->handle_fader (*fader, pos);
509 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
511 fader->set_value (pos); // alter master gain
512 _port->write (fader->set_position (pos)); // write back value (required for servo)
515 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
520 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
522 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
524 if (_mcp.device_info().no_handshake()) {
528 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
532 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
536 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
537 Fader* fader = faders[ev->note_number];
539 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
543 Strip* strip = dynamic_cast<Strip*> (&fader->group());
545 if (ev->velocity > 64) {
546 strip->handle_fader_touch (*fader, true);
548 strip->handle_fader_touch (*fader, false);
554 Button* button = buttons[ev->note_number];
558 if (ev->velocity > 64) {
562 Strip* strip = dynamic_cast<Strip*> (&button->group());
565 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
566 strip->index(), button->name(), (ev->velocity > 64)));
567 strip->handle_button (*button, ev->velocity > 64 ? press : release);
570 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
571 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
574 if (ev->velocity <= 64) {
579 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
582 /* button release should reset timer AFTER handler(s) have run */
586 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
588 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
590 if (_mcp.device_info().no_handshake()) {
594 Pot* pot = pots[ev->controller_number];
596 // bit 6 gives the sign
597 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
598 // bits 0..5 give the velocity. we interpret this as "ticks
599 // moved before this message was sent"
600 float ticks = (ev->value & 0x3f);
602 /* euphonix and perhaps other devices send zero
603 when they mean 1, we think.
609 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_CONTROL) {
610 delta = sign * (ticks / (float) 0xff);
612 delta = sign * (ticks / (float) 0x3f);
616 if (ev->controller_number == Jog::ID && _jog_wheel) {
618 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
619 _jog_wheel->jog_event (delta);
622 // add external (pedal?) control here
627 Strip* strip = dynamic_cast<Strip*> (&pot->group());
629 strip->handle_pot (*pot, delta);
634 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
636 MidiByteArray bytes (count, raw_bytes);
638 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
640 if (_mcp.device_info().no_handshake()) {
644 /* always save the device type ID so that our outgoing sysex messages
649 mackie_sysex_hdr[4] = bytes[4];
651 mackie_sysex_hdr_xt[4] = bytes[4];
657 LCP: Connection Challenge
659 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
660 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
661 write_sysex (host_connection_query (bytes));
663 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
670 case 0x03: /* LCP Connection Confirmation */
671 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
672 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
673 write_sysex (host_connection_confirmation (bytes));
678 case 0x04: /* LCP: Confirmation Denied */
679 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
683 error << "MCP: unknown sysex: " << bytes << endmsg;
688 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
691 back_insert_iterator<MidiByteArray> back (l);
692 copy (begin, end, back);
694 MidiByteArray retval;
696 // this is how to calculate the response to the challenge.
697 // from the Logic docs.
698 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
699 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
700 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
701 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
707 Surface::host_connection_query (MidiByteArray & bytes)
709 MidiByteArray response;
711 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
712 /* not a Logic Control device - no response required */
716 // handle host connection query
717 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
719 if (bytes.size() != 18) {
720 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
724 // build and send host connection reply
726 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
727 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
732 Surface::host_connection_confirmation (const MidiByteArray & bytes)
734 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
736 // decode host connection confirmation
737 if (bytes.size() != 14) {
739 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
740 throw MackieControlException (os.str());
743 // send version request
744 return MidiByteArray (2, 0x13, 0x00);
748 Surface::turn_it_on ()
756 _mcp.device_ready ();
758 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
762 update_view_mode_display ();
764 if (_mcp.device_info ().has_global_controls ()) {
765 _mcp.update_global_button (Button::Read, _mcp.metering_active ());
770 Surface::write_sysex (const MidiByteArray & mba)
777 buf << sysex_hdr() << mba << MIDI::eox;
782 Surface::write_sysex (MIDI::byte msg)
785 buf << sysex_hdr() << msg << MIDI::eox;
790 Surface::n_strips (bool with_locked_strips) const
792 if (with_locked_strips) {
793 return strips.size();
798 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
799 if (!(*it)->locked()) {
807 Surface::nth_strip (uint32_t n) const
809 if (n > n_strips()) {
818 if (_mcp.device_info().has_timecode_display ()) {
819 display_timecode (string (10, '0'), string (10, ' '));
822 if (_mcp.device_info().has_two_character_display()) {
823 show_two_char_display (string (2, '0'), string (2, ' '));
826 if (_mcp.device_info().has_master_fader () && _master_fader) {
827 _port->write (_master_fader->zero ());
831 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
839 Surface::zero_controls ()
841 if (!_mcp.device_info().has_global_controls()) {
845 // turn off global buttons and leds
847 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
848 Control & control = **it;
849 if (!control.group().is_strip()) {
850 _port->write (control.zero());
854 // and the led ring for the master strip
857 _last_master_gain_written = 0.0f;
861 Surface::periodic (uint64_t now_usecs)
863 master_gain_changed();
864 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
865 (*s)->periodic (now_usecs);
870 Surface::redisplay (ARDOUR::microseconds_t now)
872 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
873 (*s)->redisplay (now);
878 Surface::write (const MidiByteArray& data)
883 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
888 Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
890 vector<boost::shared_ptr<Route> >::const_iterator r;
891 Strips::iterator s = strips.begin();
893 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes\n", routes.size()));
895 for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
897 /* don't try to assign routes to a locked strip. it won't
898 use it anyway, but if we do, then we get out of sync
899 with the proposed mapping.
902 if (!(*s)->locked()) {
903 (*s)->set_route (*r);
908 for (; s != strips.end(); ++s) {
909 (*s)->set_route (boost::shared_ptr<Route>());
916 translate_seven_segment (char achar)
918 achar = toupper (achar);
920 if (achar >= 0x40 && achar <= 0x60) {
922 } else if (achar >= 0x21 && achar <= 0x3f) {
930 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
932 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
936 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
937 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
939 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
940 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
942 _port->write (right);
947 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
950 os << setfill('0') << setw(2) << value % 100;
951 show_two_char_display (os.str());
955 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
957 if (!_active || !_mcp.device_info().has_timecode_display()) {
960 // if there's no change, send nothing, not even sysex header
961 if (timecode == last_timecode) return;
963 // length sanity checking
964 string local_timecode = timecode;
966 // truncate to 10 characters
967 if (local_timecode.length() > 10) {
968 local_timecode = local_timecode.substr (0, 10);
971 // pad to 10 characters
972 while (local_timecode.length() < 10) {
973 local_timecode += " ";
976 // translate characters.
977 // Only the characters that actually changed are sent.
980 for (i = local_timecode.length () - 1; i >= 0; i--) {
982 if (local_timecode[i] == last_timecode[i]) {
985 MidiByteArray retval (2, 0xb0, position);
986 retval << translate_seven_segment (local_timecode[i]);
987 _port->write (retval);
992 Surface::update_flip_mode_display ()
994 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
995 (*s)->flip_mode_changed (true);
1000 Surface::update_potmode ()
1002 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1003 (*s)->potmode_changed (false);
1008 Surface::update_view_mode_display ()
1017 switch (_mcp.view_mode()) {
1018 case MackieControlProtocol::Mixer:
1019 show_two_char_display ("Mx");
1022 case MackieControlProtocol::Loop:
1023 show_two_char_display ("LP");
1026 case MackieControlProtocol::AudioTracks:
1027 show_two_char_display ("AT");
1029 case MackieControlProtocol::MidiTracks:
1030 show_two_char_display ("MT");
1038 /* we are attempting to turn a global button/LED on */
1040 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1042 if (x != controls_by_device_independent_id.end()) {
1043 Button* button = dynamic_cast<Button*> (x->second);
1045 _port->write (button->set_state (on));
1050 if (!text.empty()) {
1051 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1052 _port->write ((*s)->display (1, text));
1058 Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
1060 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1061 (*s)->gui_selection_changed (routes);
1066 Surface::say_hello ()
1068 /* wakeup for Mackie Control */
1069 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1070 _port->write (wakeup);
1071 wakeup[4] = 0x15; /* wakup Mackie XT */
1072 _port->write (wakeup);
1073 wakeup[4] = 0x10; /* wakeup Logic Control */
1074 _port->write (wakeup);
1075 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1076 _port->write (wakeup);
1080 Surface::next_jog_mode ()
1085 Surface::set_jog_mode (JogWheel::Mode)
1090 Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
1092 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1093 if ((*s)->route() == r && (*s)->locked()) {
1101 Surface::notify_metering_state_changed()
1103 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1104 (*s)->notify_metering_state_changed ();
1112 /* reset msg for Mackie Control */
1123 Surface::toggle_backlight ()
1126 int onoff = random() %2;
1128 msg << sysex_hdr ();
1130 msg << (onoff ? 0x1 : 0x0);
1137 Surface::recalibrate_faders ()
1141 msg << sysex_hdr ();
1150 Surface::set_touch_sensitivity (int sensitivity)
1152 /* NOTE: assumed called from GUI code, hence sleep() */
1154 /* sensitivity already clamped by caller */
1159 msg << sysex_hdr ();
1161 msg << 0xff; /* overwritten for each fader below */
1162 msg << (sensitivity & 0x7f);
1165 for (int fader = 0; fader < 9; ++fader) {
1173 Surface::hui_heartbeat ()
1179 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1184 Surface::connected ()
1186 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1190 if (_mcp.device_info().no_handshake()) {
1196 Surface::display_line (string const& msg, int line_num)
1198 MidiByteArray midi_msg;
1199 midi_msg << sysex_hdr ();
1201 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1203 * correspond to line
1208 midi_msg.insert (midi_msg.end(), 55, ' ');
1212 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1213 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1214 string::size_type len = ascii.length();
1217 midi_msg << ascii.substr (0, 55);
1221 for (string::size_type i = len; i < 55; ++i) {
1227 midi_msg << MIDI::eox;
1232 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1234 * @param msg is assumed to be UTF-8 encoded, and will be converted
1235 * to ASCII with an underscore as fallback character before being
1236 * sent to the device.
1239 Surface::display_message_for (string const& msg, uint64_t msecs)
1241 string::size_type newline;
1243 if ((newline = msg.find ('\n')) == string::npos) {
1245 _port->write (display_line (msg, 0));
1246 _port->write (display_line (string(), 1));
1248 } else if (newline == 0) {
1250 _port->write (display_line (string(), 0));
1251 _port->write (display_line (msg.substr (1), 1));
1255 string first_line = msg.substr (0, newline-1);
1256 string second_line = msg.substr (newline+1);
1258 _port->write (display_line (first_line, 0));
1259 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1262 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1263 (*s)->block_screen_display_for (msecs);