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 "pbd/stacktrace.h"
30 #include "midi++/port.h"
32 #include "ardour/audioengine.h"
33 #include "ardour/automation_control.h"
34 #include "ardour/debug.h"
35 #include "ardour/route.h"
36 #include "ardour/panner.h"
37 #include "ardour/panner_shell.h"
38 #include "ardour/profile.h"
39 #include "ardour/rc_configuration.h"
40 #include "ardour/session.h"
41 #include "ardour/utils.h"
43 #include <gtkmm2ext/gui_thread.h>
45 #include "control_group.h"
46 #include "surface_port.h"
49 #include "mackie_control_protocol.h"
50 #include "jog_wheel.h"
62 #ifdef PLATFORM_WINDOWS
63 #define random() rand()
68 using ARDOUR::Stripable;
70 using ARDOUR::Profile;
71 using ARDOUR::AutomationControl;
72 using namespace ArdourSurface;
73 using namespace Mackie;
75 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
77 // The MCU sysex header.4th byte Will be overwritten
78 // when we get an incoming sysex that identifies
80 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
82 // The MCU extender sysex header.4th byte Will be overwritten
83 // when we get an incoming sysex that identifies
85 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
88 // The MCU sysex header for QCon Control surface
89 static MidiByteArray mackie_sysex_hdr_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
91 // The MCU sysex header for QCon Control - extender
92 // The extender differs from Mackie by 4th bit - it's same like for main control surface (for display)
93 static MidiByteArray mackie_sysex_hdr_xt_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
96 static MidiByteArray empty_midi_byte_array;
98 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
102 , _name (device_name)
107 , _last_master_gain_written (-0.0f)
108 , connection_state (0)
111 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
114 _port = new SurfacePort (*this);
116 throw failed_constructor ();
120 if( mcp.device_info().is_qcon() ) {
126 /* only the first Surface object has global controls */
127 /* lets use master_position instead */
128 uint32_t mp = _mcp.device_info().master_position();
130 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
131 if (_mcp.device_info().has_global_controls()) {
133 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
136 if (_mcp.device_info().has_master_fader()) {
138 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
142 uint32_t n = _mcp.device_info().strip_cnt();
146 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
149 if (_mcp.device_info().uses_ipmidi()) {
150 /* ipMIDI port already exists, we can just assume that we're
153 * If the user still hasn't connected the ipMIDI surface and/or
154 * turned it on, then they have to press "Discover Mackie
155 * Devices" in the GUI at the right time.
158 connection_state |= (InputConnected|OutputConnected);
162 connect_to_signals ();
164 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
169 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
172 g_source_destroy (input_source);
176 // delete groups (strips)
177 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
181 // delete controls (global buttons, master fader etc)
182 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
188 // the ports take time to release and we may be rebuilding right away
189 // in the case of changing devices.
191 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
195 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
201 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
202 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
204 if (ni == name1 || ni == name2) {
206 connection_state |= InputConnected;
208 connection_state &= ~InputConnected;
210 } else if (no == name1 || no == name2) {
212 connection_state |= OutputConnected;
214 connection_state &= ~OutputConnected;
221 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
223 /* this will send a device query message, which should
224 result in a response that will kick off device type
225 discovery and activation of the surface(s).
227 The intended order of events is:
229 - each surface sends a device query message
230 - devices respond with either MCP or LCP response (sysex in both
232 - sysex message causes Surface::turn_it_on() which tells the
233 MCP object that the surface is ready, and sets up strip
234 displays and binds faders and buttons for that surface
236 In the case of LCP, where this is a handshake process that could
237 fail, the response process to the initial sysex after a device query
238 will mark the surface inactive, which won't shut anything down
239 but will stop any writes to the device.
241 Note: there are no known cases of the handshake process failing.
243 We actually can't initiate this in this callback, so we have
244 to queue it with the MCP event loop.
247 /* XXX this is a horrible hack. Without a short sleep here,
248 something prevents the device wakeup messages from being
249 sent and/or the responses from being received.
256 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
260 return true; /* connection status changed */
266 XMLNode* node = new XMLNode (X_("Surface"));
267 node->set_property (X_("name"), _name);
268 node->add_child_nocopy (_port->get_state());
273 Surface::set_state (const XMLNode& node, int version)
275 /* Look for a node named after the device we're part of */
277 XMLNodeList const& children = node.children();
280 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
282 if ((*c)->get_property (X_("name"), name) && name == _name) {
292 XMLNode* portnode = mynode->child (X_("Port"));
294 if (_port->set_state (*portnode, version)) {
303 Surface::sysex_hdr() const
307 if (_mcp.device_info().is_qcon()) {
308 return mackie_sysex_hdr_qcon;
310 return mackie_sysex_hdr;
313 if(_mcp.device_info().is_qcon()) {
314 return mackie_sysex_hdr_xt_qcon;
316 return mackie_sysex_hdr_xt;
319 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
320 return mackie_sysex_hdr;
323 static GlobalControlDefinition mackie_global_controls[] = {
324 { "external", Pot::External, Pot::factory, "none" },
325 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
326 { "timecode", Led::Timecode, Led::factory, "none" },
327 { "beats", Led::Beats, Led::factory, "none" },
328 { "solo", Led::RudeSolo, Led::factory, "none" },
329 { "relay_click", Led::RelayClick, Led::factory, "none" },
330 { "", 0, Led::factory, "" }
334 Surface::init_controls()
338 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
339 groups["assignment"] = new Group ("assignment");
340 groups["automation"] = new Group ("automation");
341 groups["bank"] = new Group ("bank");
342 groups["cursor"] = new Group ("cursor");
343 groups["display"] = new Group ("display");
344 groups["function select"] = new Group ("function select");
345 groups["global view"] = new Group ("global view");
346 groups["master"] = new Group ("master");
347 groups["modifiers"] = new Group ("modifiers");
348 groups["none"] = new Group ("none");
349 groups["transport"] = new Group ("transport");
350 groups["user"] = new Group ("user");
351 groups["utilities"] = new Group ("utilities");
353 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
354 if (_mcp.device_info().has_jog_wheel()) {
355 _jog_wheel = new Mackie::JogWheel (_mcp);
358 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
359 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
360 group = groups[mackie_global_controls[n].group_name];
361 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
362 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
365 /* add global buttons */
366 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
367 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
369 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
370 group = groups[b->second.group];
371 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
376 Surface::init_strips (uint32_t n)
378 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
380 for (uint32_t i = 0; i < n; ++i) {
384 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
386 Strip* strip = new Strip (*this, name, i, strip_buttons);
388 groups[name] = strip;
389 strips.push_back (strip);
394 Surface::master_monitor_may_have_changed ()
396 if (_number == _mcp.device_info().master_position()) {
402 Surface::setup_master ()
404 boost::shared_ptr<Stripable> m;
406 if ((m = _mcp.get_session().monitor_out()) == 0) {
407 m = _mcp.get_session().master_out();
412 _master_fader->set_control (boost::shared_ptr<AutomationControl>());
414 master_connection.disconnect ();
418 if (!_master_fader) {
419 Groups::iterator group_it;
421 group_it = groups.find("master");
423 if (group_it == groups.end()) {
424 groups["master"] = master_group = new Group ("master");
426 master_group = group_it->second;
429 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *master_group));
431 DeviceInfo device_info = _mcp.device_info();
432 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
433 Button* bb = dynamic_cast<Button*> (Button::factory (
435 Button::MasterFaderTouch,
441 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
442 number(), Button::MasterFaderTouch, bb->id()));
444 master_connection.disconnect ();
447 _master_fader->set_control (m->gain_control());
448 m->gain_control()->Changed.connect (master_connection, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
449 _last_master_gain_written = FLT_MAX; /* some essentially impossible value */
450 master_gain_changed ();
454 Surface::master_gain_changed ()
456 if (!_master_fader) {
460 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
465 float normalized_position = ac->internal_to_interface (ac->get_value());
466 if (normalized_position == _last_master_gain_written) {
470 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
472 _port->write (_master_fader->set_position (normalized_position));
473 _last_master_gain_written = normalized_position;
477 Surface::scaled_delta (float delta, float current_speed)
479 /* XXX needs work before use */
480 const float sign = delta < 0.0 ? -1.0 : 1.0;
482 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
486 Surface::display_bank_start (uint32_t current_bank)
488 if (current_bank == 0) {
489 // send Ar. to 2-char display on the master
490 show_two_char_display ("Ar", "..");
492 // write the current first remote_id to the 2-char display
493 show_two_char_display (current_bank);
498 Surface::blank_jog_ring ()
500 Control* control = controls_by_device_independent_id[Jog::ID];
503 Pot* pot = dynamic_cast<Pot*> (control);
505 _port->write (pot->set (0.0, false, Pot::spread));
511 Surface::scrub_scaling_factor () const
517 Surface::connect_to_signals ()
522 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
523 number(), _port->input_port().name()));
525 MIDI::Parser* p = _port->input_port().parser();
528 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
529 /* V-Pot messages are Controller */
530 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
531 /* Button messages are NoteOn */
532 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
533 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
534 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
535 /* Fader messages are Pitchbend */
537 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
538 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
541 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()));
548 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
550 /* Pitchbend messages are fader position messages. Nothing in the data we get
551 * from the MIDI::Parser conveys the fader ID, which was given by the
552 * channel ID in the status byte.
554 * Instead, we have used bind() to supply the fader-within-strip ID
555 * when we connected to the per-channel pitchbend events.
558 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
559 fader_id, pb, _number, pb/16384.0));
561 if (_mcp.device_info().no_handshake()) {
565 Fader* fader = faders[fader_id];
568 Strip* strip = dynamic_cast<Strip*> (&fader->group());
569 float pos = pb / 16384.0;
571 strip->handle_fader (*fader, pos);
573 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
575 fader->set_value (pos); // alter master gain
576 _port->write (fader->set_position (pos)); // write back value (required for servo)
579 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
584 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
586 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
588 if (_mcp.device_info().no_handshake()) {
592 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
596 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
600 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
601 Fader* fader = faders[ev->note_number];
603 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
607 Strip* strip = dynamic_cast<Strip*> (&fader->group());
609 if (ev->velocity > 64) {
610 strip->handle_fader_touch (*fader, true);
612 strip->handle_fader_touch (*fader, false);
618 Button* button = buttons[ev->note_number];
622 if (ev->velocity > 64) {
626 Strip* strip = dynamic_cast<Strip*> (&button->group());
629 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
630 strip->index(), button->name(), (ev->velocity > 64)));
631 strip->handle_button (*button, ev->velocity > 64 ? press : release);
634 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
635 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
638 if (ev->velocity <= 64) {
643 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
646 /* button release should reset timer AFTER handler(s) have run */
650 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
652 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
654 if (_mcp.device_info().no_handshake()) {
658 Pot* pot = pots[ev->controller_number];
660 // bit 6 gives the sign
661 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
662 // bits 0..5 give the velocity. we interpret this as "ticks
663 // moved before this message was sent"
664 float ticks = (ev->value & 0x3f);
666 /* euphonix and perhaps other devices send zero
667 when they mean 1, we think.
673 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
674 delta = sign * (ticks / (float) 0xff);
676 delta = sign * (ticks / (float) 0x3f);
680 if (ev->controller_number == Jog::ID && _jog_wheel) {
682 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
683 _jog_wheel->jog_event (delta);
686 // add external (pedal?) control here
691 Strip* strip = dynamic_cast<Strip*> (&pot->group());
693 strip->handle_pot (*pot, delta);
698 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
700 MidiByteArray bytes (count, raw_bytes);
702 if (_mcp.device_info().no_handshake()) {
706 /* always save the device type ID so that our outgoing sysex messages
711 if (_mcp.device_info().is_qcon()) {
712 mackie_sysex_hdr_qcon[4] = bytes[4];
714 mackie_sysex_hdr[4] = bytes[4];
718 if (_mcp.device_info().is_qcon()) {
719 mackie_sysex_hdr_xt_qcon[4] = bytes[4];
721 mackie_sysex_hdr_xt[4] = bytes[4];
728 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
731 LCP: Connection Challenge
733 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
734 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
735 write_sysex (host_connection_query (bytes));
738 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
746 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
748 /* Behringer X-Touch Compact: Device Ready
750 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Behringer X-Touch Compact ready, current status = %1\n", _active));
754 case 0x03: /* LCP Connection Confirmation */
755 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
756 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
757 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
758 write_sysex (host_connection_confirmation (bytes));
763 case 0x04: /* LCP: Confirmation Denied */
764 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
765 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
770 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
771 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("unknown device ID byte %1", (int) bytes[5]));
772 error << "MCP: unknown sysex: " << bytes << endmsg;
777 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
780 back_insert_iterator<MidiByteArray> back (l);
781 copy (begin, end, back);
783 MidiByteArray retval;
785 // this is how to calculate the response to the challenge.
786 // from the Logic docs.
787 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
788 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
789 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
790 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
796 Surface::host_connection_query (MidiByteArray & bytes)
798 MidiByteArray response;
800 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
801 /* not a Logic Control device - no response required */
805 // handle host connection query
806 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
808 if (bytes.size() != 18) {
809 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
813 // build and send host connection reply
815 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
816 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
821 Surface::host_connection_confirmation (const MidiByteArray & bytes)
823 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
825 // decode host connection confirmation
826 if (bytes.size() != 14) {
828 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
829 throw MackieControlException (os.str());
832 // send version request
833 return MidiByteArray (2, 0x13, 0x00);
837 Surface::turn_it_on ()
845 _mcp.device_ready ();
847 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
851 update_view_mode_display (false);
853 // if (_mcp.device_info ().has_global_controls ()) {
854 // _mcp.update_global_button (Button::Read, _mcp.metering_active ());
859 Surface::write_sysex (const MidiByteArray & mba)
866 buf << sysex_hdr() << mba << MIDI::eox;
871 Surface::write_sysex (MIDI::byte msg)
874 buf << sysex_hdr() << msg << MIDI::eox;
879 Surface::n_strips (bool with_locked_strips) const
881 if (with_locked_strips) {
882 return strips.size();
887 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
888 if (!(*it)->locked()) {
896 Surface::nth_strip (uint32_t n) const
898 if (n > n_strips()) {
907 if (_mcp.device_info().has_timecode_display ()) {
908 display_timecode (string (10, '0'), string (10, ' '));
911 if (_mcp.device_info().has_two_character_display()) {
912 show_two_char_display (string (2, '0'), string (2, ' '));
915 if (_mcp.device_info().has_master_fader () && _master_fader) {
916 _port->write (_master_fader->zero ());
920 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
928 Surface::zero_controls ()
930 if (!_mcp.device_info().has_global_controls()) {
934 // turn off global buttons and leds
936 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
937 Control & control = **it;
938 if (!control.group().is_strip()) {
939 _port->write (control.zero());
943 // and the led ring for the master strip
946 _last_master_gain_written = 0.0f;
950 Surface::periodic (uint64_t now_usecs)
952 master_gain_changed();
953 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
954 (*s)->periodic (now_usecs);
959 Surface::redisplay (ARDOUR::microseconds_t now, bool force)
961 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
962 (*s)->redisplay (now, force);
967 Surface::write (const MidiByteArray& data)
972 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
977 Surface::update_strip_selection ()
979 Strips::iterator s = strips.begin();
980 for ( ; s != strips.end(); ++s) {
981 (*s)->update_selection_state();
986 Surface::map_stripables (const vector<boost::shared_ptr<Stripable> >& stripables)
988 vector<boost::shared_ptr<Stripable> >::const_iterator r;
989 Strips::iterator s = strips.begin();
991 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 stripables to %2 strips\n", stripables.size(), strips.size()));
993 for (r = stripables.begin(); r != stripables.end() && s != strips.end(); ++s) {
995 /* don't try to assign stripables to a locked strip. it won't
996 use it anyway, but if we do, then we get out of sync
997 with the proposed mapping.
1000 if (!(*s)->locked()) {
1001 (*s)->set_stripable (*r);
1006 for (; s != strips.end(); ++s) {
1007 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 being set to null stripable\n", (*s)->index()));
1008 (*s)->set_stripable (boost::shared_ptr<Stripable>());
1013 translate_seven_segment (char achar)
1015 achar = toupper (achar);
1017 if (achar >= 0x40 && achar <= 0x60) {
1018 return achar - 0x40;
1019 } else if (achar >= 0x21 && achar <= 0x3f) {
1027 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
1029 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
1033 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
1034 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
1036 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
1037 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
1039 _port->write (right);
1040 _port->write (left);
1044 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
1047 os << setfill('0') << setw(2) << value % 100;
1048 show_two_char_display (os.str());
1052 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
1054 //TODO: Fix for Qcon to correct timecode value if is over 1000 bars
1056 if (!_active || !_mcp.device_info().has_timecode_display()) {
1059 // if there's no change, send nothing, not even sysex header
1060 if (timecode == last_timecode) return;
1062 // length sanity checking
1063 string local_timecode = timecode;
1065 // truncate to 10 characters
1066 if (local_timecode.length() > 10) {
1067 local_timecode = local_timecode.substr (0, 10);
1070 // pad to 10 characters
1071 while (local_timecode.length() < 10) {
1072 local_timecode += " ";
1075 // translate characters.
1076 // Only the characters that actually changed are sent.
1077 int position = 0x3f;
1079 for (i = local_timecode.length () - 1; i >= 0; i--) {
1081 if (local_timecode[i] == last_timecode[i]) {
1084 MidiByteArray retval (2, 0xb0, position);
1085 retval << translate_seven_segment (local_timecode[i]);
1086 _port->write (retval);
1091 Surface::update_flip_mode_display ()
1093 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1094 (*s)->flip_mode_changed ();
1099 Surface::subview_mode_changed ()
1101 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1102 (*s)->subview_mode_changed ();
1107 Surface::update_view_mode_display (bool with_helpful_text)
1116 switch (_mcp.view_mode()) {
1117 case MackieControlProtocol::Mixer:
1118 show_two_char_display ("Mx");
1120 text = _("Mixer View");
1122 case MackieControlProtocol::AudioTracks:
1123 show_two_char_display ("AT");
1124 id = Button::AudioTracks;
1125 text = _("Audio Tracks");
1127 case MackieControlProtocol::MidiTracks:
1128 show_two_char_display ("MT");
1129 id = Button::MidiTracks;
1130 text = _("MIDI Tracks");
1132 case MackieControlProtocol::Plugins:
1133 show_two_char_display ("PL");
1134 id = Button::Plugin;
1135 text = _("Plugins");
1137 case MackieControlProtocol::Busses:
1138 show_two_char_display ("BS");
1139 id = Button::Busses;
1140 if (Profile->get_mixbus()) {
1141 text = _("Mixbusses");
1146 case MackieControlProtocol::Auxes:
1147 show_two_char_display ("Au");
1151 case MackieControlProtocol::Hidden:
1152 show_two_char_display ("HI");
1153 id = Button::Outputs;
1154 text = _("Hidden Tracks");
1156 case MackieControlProtocol::Selected:
1157 show_two_char_display ("SE");
1159 text = _("Selected Tracks");
1165 vector<int> view_mode_buttons;
1166 view_mode_buttons.push_back (Button::View);
1167 view_mode_buttons.push_back (Button::Busses);
1168 view_mode_buttons.push_back (Button::Plugin);
1169 view_mode_buttons.push_back (Button::AudioTracks);
1170 view_mode_buttons.push_back (Button::MidiTracks);
1171 view_mode_buttons.push_back (Button::Aux);
1172 view_mode_buttons.push_back (Button::Outputs);
1173 view_mode_buttons.push_back (Button::User);
1177 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1178 map<int,Control*>::iterator x = controls_by_device_independent_id.find (id);
1180 if (x != controls_by_device_independent_id.end()) {
1181 Button* button = dynamic_cast<Button*> (x->second);
1186 _port->write (button->set_state (onoff));
1192 if (with_helpful_text && !text.empty()) {
1193 display_message_for (text, 1000);
1198 Surface::say_hello ()
1200 /* wakeup for Mackie Control */
1201 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1202 _port->write (wakeup);
1203 wakeup[4] = 0x15; /* wakup Mackie XT */
1204 _port->write (wakeup);
1205 wakeup[4] = 0x10; /* wakeup Logic Control */
1206 _port->write (wakeup);
1207 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1208 _port->write (wakeup);
1212 Surface::next_jog_mode ()
1217 Surface::set_jog_mode (JogWheel::Mode)
1222 Surface::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> stripable) const
1224 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1225 if ((*s)->stripable() == stripable && (*s)->locked()) {
1233 Surface::stripable_is_mapped (boost::shared_ptr<Stripable> stripable) const
1235 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1236 if ((*s)->stripable() == stripable) {
1245 Surface::notify_metering_state_changed()
1247 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1248 (*s)->notify_metering_state_changed ();
1256 /* reset msg for Mackie Control */
1267 Surface::toggle_backlight ()
1270 int onoff = random() %2;
1272 msg << sysex_hdr ();
1274 msg << (onoff ? 0x1 : 0x0);
1281 Surface::recalibrate_faders ()
1285 msg << sysex_hdr ();
1294 Surface::set_touch_sensitivity (int sensitivity)
1296 /* NOTE: assumed called from GUI code, hence sleep() */
1298 /* sensitivity already clamped by caller */
1300 if( !is_qcon ) { // Qcon doesn't support fader sensitivity
1304 msg << sysex_hdr ();
1306 msg << 0xff; /* overwritten for each fader below */
1307 msg << (sensitivity & 0x7f);
1310 for (int fader = 0; fader < 9; ++fader) {
1319 Surface::hui_heartbeat ()
1325 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1330 Surface::connected ()
1332 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1336 if (_mcp.device_info().no_handshake()) {
1342 Surface::display_line (string const& msg, int line_num)
1344 MidiByteArray midi_msg;
1345 midi_msg << sysex_hdr ();
1347 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1349 * correspond to line
1354 midi_msg.insert (midi_msg.end(), 55, ' ');
1358 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1359 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1360 string::size_type len = ascii.length();
1363 midi_msg << ascii.substr (0, 55);
1367 for (string::size_type i = len; i < 55; ++i) {
1373 midi_msg << MIDI::eox;
1378 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1380 * @param msg is assumed to be UTF-8 encoded, and will be converted
1381 * to ASCII with an underscore as fallback character before being
1382 * sent to the device.
1385 Surface::display_message_for (string const& msg, uint64_t msecs)
1387 string::size_type newline;
1389 if ((newline = msg.find ('\n')) == string::npos) {
1391 _port->write (display_line (msg, 0));
1392 _port->write (display_line (string(), 1));
1394 } else if (newline == 0) {
1396 _port->write (display_line (string(), 0));
1397 _port->write (display_line (msg.substr (1), 1));
1401 string first_line = msg.substr (0, newline-1);
1402 string second_line = msg.substr (newline+1);
1404 _port->write (display_line (first_line, 0));
1405 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1408 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1409 (*s)->block_screen_display_for (msecs);