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 <gtkmm2ext/gui_thread.h>
42 #include "control_group.h"
43 #include "surface_port.h"
46 #include "mackie_control_protocol.h"
47 #include "jog_wheel.h"
59 #ifdef PLATFORM_WINDOWS
60 #define random() rand()
67 using ARDOUR::Pannable;
68 using ARDOUR::AutomationControl;
69 using namespace ArdourSurface;
70 using namespace Mackie;
72 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
74 // The MCU sysex header.4th byte Will be overwritten
75 // when we get an incoming sysex that identifies
77 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
79 // The MCU extender sysex header.4th byte Will be overwritten
80 // when we get an incoming sysex that identifies
82 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
84 static MidiByteArray empty_midi_byte_array;
86 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
95 , _last_master_gain_written (-0.0f)
96 , connection_state (0)
99 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
102 _port = new SurfacePort (*this);
104 throw failed_constructor ();
107 /* only the first Surface object has global controls */
108 /* lets use master_position instead */
109 uint32_t mp = _mcp.device_info().master_position();
111 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
112 if (_mcp.device_info().has_global_controls()) {
114 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
117 if (_mcp.device_info().has_master_fader()) {
119 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
123 uint32_t n = _mcp.device_info().strip_cnt();
127 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
130 if (_mcp.device_info().uses_ipmidi()) {
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");
153 g_source_destroy (input_source);
157 // delete groups (strips)
158 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
162 // delete controls (global buttons, master fader etc)
163 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
169 // the ports take time to release and we may be rebuilding right away
170 // in the case of changing devices.
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;
202 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
204 /* this will send a device query message, which should
205 result in a response that will kick off device type
206 discovery and activation of the surface(s).
208 The intended order of events is:
210 - each surface sends a device query message
211 - devices respond with either MCP or LCP response (sysex in both
213 - sysex message causes Surface::turn_it_on() which tells the
214 MCP object that the surface is ready, and sets up strip
215 displays and binds faders and buttons for that surface
217 In the case of LCP, where this is a handshake process that could
218 fail, the response process to the initial sysex after a device query
219 will mark the surface inactive, which won't shut anything down
220 but will stop any writes to the device.
222 Note: there are no known cases of the handshake process failing.
224 We actually can't initiate this in this callback, so we have
225 to queue it with the MCP event loop.
228 /* XXX this is a horrible hack. Without a short sleep here,
229 something prevents the device wakeup messages from being
230 sent and/or the responses from being received.
237 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
241 return true; /* connection status changed */
247 XMLNode* node = new XMLNode (X_("Surface"));
248 node->add_property (X_("name"), _name);
249 node->add_child_nocopy (_port->get_state());
254 Surface::set_state (const XMLNode& node, int version)
256 /* Look for a node named after the device we're part of */
258 XMLNodeList const& children = node.children();
261 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
262 XMLProperty const* prop = (*c)->property (X_("name"));
264 if (prop->value() == _name) {
275 XMLNode* portnode = mynode->child (X_("Port"));
277 if (_port->set_state (*portnode, version)) {
286 Surface::sysex_hdr() const
289 case mcu: return mackie_sysex_hdr;
290 case ext: return mackie_sysex_hdr_xt;
292 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
293 return mackie_sysex_hdr;
296 static GlobalControlDefinition mackie_global_controls[] = {
297 { "external", Pot::External, Pot::factory, "none" },
298 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
299 { "timecode", Led::Timecode, Led::factory, "none" },
300 { "beats", Led::Beats, Led::factory, "none" },
301 { "solo", Led::RudeSolo, Led::factory, "none" },
302 { "relay_click", Led::RelayClick, Led::factory, "none" },
303 { "", 0, Led::factory, "" }
307 Surface::init_controls()
311 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
312 groups["assignment"] = new Group ("assignment");
313 groups["automation"] = new Group ("automation");
314 groups["bank"] = new Group ("bank");
315 groups["cursor"] = new Group ("cursor");
316 groups["display"] = new Group ("display");
317 groups["function select"] = new Group ("function select");
318 groups["global view"] = new Group ("global view");
319 groups["master"] = new Group ("master");
320 groups["modifiers"] = new Group ("modifiers");
321 groups["none"] = new Group ("none");
322 groups["transport"] = new Group ("transport");
323 groups["user"] = new Group ("user");
324 groups["utilities"] = new Group ("utilities");
326 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
327 if (_mcp.device_info().has_jog_wheel()) {
328 _jog_wheel = new Mackie::JogWheel (_mcp);
331 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
332 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
333 group = groups[mackie_global_controls[n].group_name];
334 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
335 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
338 /* add global buttons */
339 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
340 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
342 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
343 group = groups[b->second.group];
344 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
349 Surface::init_strips (uint32_t n)
351 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
353 for (uint32_t i = 0; i < n; ++i) {
357 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
359 Strip* strip = new Strip (*this, name, i, strip_buttons);
361 groups[name] = strip;
362 strips.push_back (strip);
367 Surface::setup_master ()
369 boost::shared_ptr<Route> m;
371 if ((m = _mcp.get_session().monitor_out()) == 0) {
372 m = _mcp.get_session().master_out();
379 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
381 _master_fader->set_control (m->gain_control());
382 m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
384 Groups::iterator group_it;
385 group_it = groups.find("master");
387 DeviceInfo device_info = _mcp.device_info();
388 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
389 Button* bb = dynamic_cast<Button*> (Button::factory (
391 Button::MasterFaderTouch,
396 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
397 number(), Button::MasterFaderTouch, bb->id()));
401 Surface::master_gain_changed ()
403 if (!_master_fader) {
407 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
412 float normalized_position = ac->internal_to_interface (ac->get_value());
413 if (normalized_position == _last_master_gain_written) {
417 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
419 _port->write (_master_fader->set_position (normalized_position));
420 _last_master_gain_written = normalized_position;
424 Surface::scaled_delta (float delta, float current_speed)
426 /* XXX needs work before use */
427 const float sign = delta < 0.0 ? -1.0 : 1.0;
429 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
433 Surface::display_bank_start (uint32_t current_bank)
435 if (current_bank == 0) {
436 // send Ar. to 2-char display on the master
437 show_two_char_display ("Ar", "..");
439 // write the current first remote_id to the 2-char display
440 show_two_char_display (current_bank);
445 Surface::blank_jog_ring ()
447 Control* control = controls_by_device_independent_id[Jog::ID];
450 Pot* pot = dynamic_cast<Pot*> (control);
452 _port->write (pot->set (0.0, false, Pot::spread));
458 Surface::scrub_scaling_factor () const
464 Surface::connect_to_signals ()
469 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
470 number(), _port->input_port().name()));
472 MIDI::Parser* p = _port->input_port().parser();
475 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
476 /* V-Pot messages are Controller */
477 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
478 /* Button messages are NoteOn */
479 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
480 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
481 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
482 /* Fader messages are Pitchbend */
484 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
485 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
488 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()));
495 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
497 /* Pitchbend messages are fader position messages. Nothing in the data we get
498 * from the MIDI::Parser conveys the fader ID, which was given by the
499 * channel ID in the status byte.
501 * Instead, we have used bind() to supply the fader-within-strip ID
502 * when we connected to the per-channel pitchbend events.
505 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
506 fader_id, pb, _number, pb/16384.0));
508 if (_mcp.device_info().no_handshake()) {
512 if (_mcp.main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
513 /* user is doing a reset to unity gain but device sends a PB
514 * message in the middle of the touch on/off messages. Ignore
520 Fader* fader = faders[fader_id];
523 Strip* strip = dynamic_cast<Strip*> (&fader->group());
524 float pos = pb / 16384.0;
526 strip->handle_fader (*fader, pos);
528 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
530 fader->set_value (pos); // alter master gain
531 _port->write (fader->set_position (pos)); // write back value (required for servo)
534 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
539 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
541 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
543 if (_mcp.device_info().no_handshake()) {
547 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
551 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
555 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
556 Fader* fader = faders[ev->note_number];
558 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
562 Strip* strip = dynamic_cast<Strip*> (&fader->group());
564 if (ev->velocity > 64) {
565 strip->handle_fader_touch (*fader, true);
567 strip->handle_fader_touch (*fader, false);
573 Button* button = buttons[ev->note_number];
577 if (ev->velocity > 64) {
581 Strip* strip = dynamic_cast<Strip*> (&button->group());
584 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
585 strip->index(), button->name(), (ev->velocity > 64)));
586 strip->handle_button (*button, ev->velocity > 64 ? press : release);
589 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
590 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
593 if (ev->velocity <= 64) {
598 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
601 /* button release should reset timer AFTER handler(s) have run */
605 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
607 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
609 if (_mcp.device_info().no_handshake()) {
613 Pot* pot = pots[ev->controller_number];
615 // bit 6 gives the sign
616 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
617 // bits 0..5 give the velocity. we interpret this as "ticks
618 // moved before this message was sent"
619 float ticks = (ev->value & 0x3f);
621 /* euphonix and perhaps other devices send zero
622 when they mean 1, we think.
628 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_CONTROL) {
629 delta = sign * (ticks / (float) 0xff);
631 delta = sign * (ticks / (float) 0x3f);
635 if (ev->controller_number == Jog::ID && _jog_wheel) {
637 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
638 _jog_wheel->jog_event (delta);
641 // add external (pedal?) control here
646 Strip* strip = dynamic_cast<Strip*> (&pot->group());
648 strip->handle_pot (*pot, delta);
653 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
655 MidiByteArray bytes (count, raw_bytes);
657 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
659 if (_mcp.device_info().no_handshake()) {
663 /* always save the device type ID so that our outgoing sysex messages
668 mackie_sysex_hdr[4] = bytes[4];
670 mackie_sysex_hdr_xt[4] = bytes[4];
676 LCP: Connection Challenge
678 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
679 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
680 write_sysex (host_connection_query (bytes));
682 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
687 case 0x03: /* LCP Connection Confirmation */
688 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
689 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
690 write_sysex (host_connection_confirmation (bytes));
695 case 0x04: /* LCP: Confirmation Denied */
696 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
700 error << "MCP: unknown sysex: " << bytes << endmsg;
705 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
708 back_insert_iterator<MidiByteArray> back (l);
709 copy (begin, end, back);
711 MidiByteArray retval;
713 // this is how to calculate the response to the challenge.
714 // from the Logic docs.
715 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
716 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
717 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
718 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
724 Surface::host_connection_query (MidiByteArray & bytes)
726 MidiByteArray response;
728 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
729 /* not a Logic Control device - no response required */
733 // handle host connection query
734 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
736 if (bytes.size() != 18) {
737 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
741 // build and send host connection reply
743 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
744 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
749 Surface::host_connection_confirmation (const MidiByteArray & bytes)
751 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
753 // decode host connection confirmation
754 if (bytes.size() != 14) {
756 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
757 throw MackieControlException (os.str());
760 // send version request
761 return MidiByteArray (2, 0x13, 0x00);
765 Surface::turn_it_on ()
773 _mcp.device_ready ();
775 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
779 update_view_mode_display ();
781 if (_mcp.device_info ().has_global_controls ()) {
782 _mcp.update_global_button (Button::Read, _mcp.metering_active ());
787 Surface::write_sysex (const MidiByteArray & mba)
794 buf << sysex_hdr() << mba << MIDI::eox;
799 Surface::write_sysex (MIDI::byte msg)
802 buf << sysex_hdr() << msg << MIDI::eox;
807 Surface::n_strips (bool with_locked_strips) const
809 if (with_locked_strips) {
810 return strips.size();
815 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
816 if (!(*it)->locked()) {
824 Surface::nth_strip (uint32_t n) const
826 if (n > n_strips()) {
835 if (_mcp.device_info().has_timecode_display ()) {
836 display_timecode (string (10, '0'), string (10, ' '));
839 if (_mcp.device_info().has_two_character_display()) {
840 show_two_char_display (string (2, '0'), string (2, ' '));
843 if (_mcp.device_info().has_master_fader () && _master_fader) {
844 _port->write (_master_fader->zero ());
848 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
856 Surface::zero_controls ()
858 if (!_mcp.device_info().has_global_controls()) {
862 // turn off global buttons and leds
864 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
865 Control & control = **it;
866 if (!control.group().is_strip()) {
867 _port->write (control.zero());
871 // and the led ring for the master strip
874 _last_master_gain_written = 0.0f;
878 Surface::periodic (uint64_t now_usecs)
880 master_gain_changed();
881 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
882 (*s)->periodic (now_usecs);
887 Surface::redisplay (ARDOUR::microseconds_t now)
889 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
890 (*s)->redisplay (now);
895 Surface::write (const MidiByteArray& data)
900 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
905 Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
907 vector<boost::shared_ptr<Route> >::const_iterator r;
908 Strips::iterator s = strips.begin();
910 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 routes\n", routes.size()));
912 for (r = routes.begin(); r != routes.end() && s != strips.end(); ++s) {
914 /* don't try to assign routes to a locked strip. it won't
915 use it anyway, but if we do, then we get out of sync
916 with the proposed mapping.
919 if (!(*s)->locked()) {
920 (*s)->set_route (*r);
925 for (; s != strips.end(); ++s) {
926 (*s)->set_route (boost::shared_ptr<Route>());
933 translate_seven_segment (char achar)
935 achar = toupper (achar);
937 if (achar >= 0x40 && achar <= 0x60) {
939 } else if (achar >= 0x21 && achar <= 0x3f) {
947 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
949 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
953 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
954 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
956 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
957 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
959 _port->write (right);
964 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
967 os << setfill('0') << setw(2) << value % 100;
968 show_two_char_display (os.str());
972 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
974 if (!_active || !_mcp.device_info().has_timecode_display()) {
977 // if there's no change, send nothing, not even sysex header
978 if (timecode == last_timecode) return;
980 // length sanity checking
981 string local_timecode = timecode;
983 // truncate to 10 characters
984 if (local_timecode.length() > 10) {
985 local_timecode = local_timecode.substr (0, 10);
988 // pad to 10 characters
989 while (local_timecode.length() < 10) {
990 local_timecode += " ";
993 // translate characters.
994 // Only the characters that actually changed are sent.
997 for (i = local_timecode.length () - 1; i >= 0; i--) {
999 if (local_timecode[i] == last_timecode[i]) {
1002 MidiByteArray retval (2, 0xb0, position);
1003 retval << translate_seven_segment (local_timecode[i]);
1004 _port->write (retval);
1009 Surface::update_flip_mode_display ()
1011 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1012 (*s)->potmode_changed (true);
1017 Surface::update_potmode ()
1019 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1020 (*s)->potmode_changed (false);
1025 Surface::update_view_mode_display ()
1034 switch (_mcp.view_mode()) {
1035 case MackieControlProtocol::Mixer:
1036 show_two_char_display ("Mx");
1038 text = _("Mixer View");
1040 case MackieControlProtocol::AudioTracks:
1041 show_two_char_display ("AT");
1042 id = Button::AudioTracks;
1043 text = _("Audio Tracks");
1045 case MackieControlProtocol::MidiTracks:
1046 show_two_char_display ("MT");
1047 id = Button::MidiTracks;
1048 text = _("MIDI Tracks");
1050 case MackieControlProtocol::Plugins:
1051 show_two_char_display ("PL");
1052 id = Button::Plugin;
1053 text = _("Plugins");
1055 case MackieControlProtocol::Busses:
1056 show_two_char_display ("BS");
1057 id = Button::Busses;
1060 case MackieControlProtocol::Auxes:
1061 show_two_char_display ("AB");
1069 vector<int> view_mode_buttons;
1070 view_mode_buttons.push_back (Button::View);
1071 view_mode_buttons.push_back (Button::Busses);
1072 view_mode_buttons.push_back (Button::Plugin);
1073 view_mode_buttons.push_back (Button::AudioTracks);
1074 view_mode_buttons.push_back (Button::MidiTracks);
1075 view_mode_buttons.push_back (Button::Aux);
1079 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1080 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1082 if (x != controls_by_device_independent_id.end()) {
1083 Button* button = dynamic_cast<Button*> (x->second);
1088 _port->write (button->set_state (onoff));
1094 if (!text.empty()) {
1095 display_message_for (text, 1000);
1100 Surface::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& routes)
1102 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1103 (*s)->gui_selection_changed (routes);
1108 Surface::say_hello ()
1110 /* wakeup for Mackie Control */
1111 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1112 _port->write (wakeup);
1113 wakeup[4] = 0x15; /* wakup Mackie XT */
1114 _port->write (wakeup);
1115 wakeup[4] = 0x10; /* wakeup Logic Control */
1116 _port->write (wakeup);
1117 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1118 _port->write (wakeup);
1122 Surface::next_jog_mode ()
1127 Surface::set_jog_mode (JogWheel::Mode)
1132 Surface::route_is_locked_to_strip (boost::shared_ptr<Route> r) const
1134 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1135 if ((*s)->route() == r && (*s)->locked()) {
1143 Surface::notify_metering_state_changed()
1145 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1146 (*s)->notify_metering_state_changed ();
1154 /* reset msg for Mackie Control */
1165 Surface::toggle_backlight ()
1168 int onoff = random() %2;
1170 msg << sysex_hdr ();
1172 msg << (onoff ? 0x1 : 0x0);
1179 Surface::recalibrate_faders ()
1183 msg << sysex_hdr ();
1192 Surface::set_touch_sensitivity (int sensitivity)
1194 /* NOTE: assumed called from GUI code, hence sleep() */
1196 /* sensitivity already clamped by caller */
1201 msg << sysex_hdr ();
1203 msg << 0xff; /* overwritten for each fader below */
1204 msg << (sensitivity & 0x7f);
1207 for (int fader = 0; fader < 9; ++fader) {
1215 Surface::hui_heartbeat ()
1221 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1226 Surface::connected ()
1228 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1232 if (_mcp.device_info().no_handshake()) {
1238 Surface::display_line (string const& msg, int line_num)
1240 MidiByteArray midi_msg;
1241 midi_msg << sysex_hdr ();
1243 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1245 * correspond to line
1250 midi_msg.insert (midi_msg.end(), 55, ' ');
1254 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1255 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1256 string::size_type len = ascii.length();
1259 midi_msg << ascii.substr (0, 55);
1263 for (string::size_type i = len; i < 55; ++i) {
1269 midi_msg << MIDI::eox;
1274 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1276 * @param msg is assumed to be UTF-8 encoded, and will be converted
1277 * to ASCII with an underscore as fallback character before being
1278 * sent to the device.
1281 Surface::display_message_for (string const& msg, uint64_t msecs)
1283 string::size_type newline;
1285 if ((newline = msg.find ('\n')) == string::npos) {
1287 _port->write (display_line (msg, 0));
1288 _port->write (display_line (string(), 1));
1290 } else if (newline == 0) {
1292 _port->write (display_line (string(), 0));
1293 _port->write (display_line (msg.substr (1), 1));
1297 string first_line = msg.substr (0, newline-1);
1298 string second_line = msg.substr (newline+1);
1300 _port->write (display_line (first_line, 0));
1301 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1304 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1305 (*s)->block_screen_display_for (msecs);