2 * Copyright (C) 2006-2007 John Anderson
3 * Copyright (C) 2008-2016 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2008 Doug McLain <doug@nostar.net>
5 * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
6 * Copyright (C) 2015-2016 Len Ovens <len@ovenwerks.net>
7 * Copyright (C) 2015-2016 Robin Gareus <robin@gareus.org>
8 * Copyright (C) 2016-2018 Ben Loftis <ben@harrisonconsoles.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <glibmm/convert.h>
33 #include "pbd/stacktrace.h"
35 #include "midi++/port.h"
37 #include "ardour/audioengine.h"
38 #include "ardour/automation_control.h"
39 #include "ardour/debug.h"
40 #include "ardour/route.h"
41 #include "ardour/panner.h"
42 #include "ardour/panner_shell.h"
43 #include "ardour/profile.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/session.h"
46 #include "ardour/utils.h"
48 #include <gtkmm2ext/gui_thread.h>
50 #include "control_group.h"
51 #include "surface_port.h"
54 #include "mackie_control_protocol.h"
55 #include "jog_wheel.h"
67 #ifdef PLATFORM_WINDOWS
68 #define random() rand()
73 using ARDOUR::Stripable;
75 using ARDOUR::Profile;
76 using ARDOUR::AutomationControl;
77 using namespace ArdourSurface;
78 using namespace Mackie;
80 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
82 // The MCU sysex header.4th byte Will be overwritten
83 // when we get an incoming sysex that identifies
85 static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
87 // The MCU extender sysex header.4th byte Will be overwritten
88 // when we get an incoming sysex that identifies
90 static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15);
93 // The MCU sysex header for QCon Control surface
94 static MidiByteArray mackie_sysex_hdr_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
96 // The MCU sysex header for QCon Control - extender
97 // The extender differs from Mackie by 4th bit - it's same like for main control surface (for display)
98 static MidiByteArray mackie_sysex_hdr_xt_qcon (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14);
101 static MidiByteArray empty_midi_byte_array;
103 Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype)
107 , _name (device_name)
112 , _last_master_gain_written (-0.0f)
113 , connection_state (0)
117 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
120 _port = new SurfacePort (*this);
122 throw failed_constructor ();
126 if( mcp.device_info().is_qcon() ) {
132 /* only the first Surface object has global controls */
133 /* lets use master_position instead */
134 uint32_t mp = _mcp.device_info().master_position();
136 DEBUG_TRACE (DEBUG::MackieControl, "Surface matches MasterPosition. Might have global controls.\n");
137 if (_mcp.device_info().has_global_controls()) {
139 DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
142 if (_mcp.device_info().has_master_fader()) {
144 DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
148 uint32_t n = _mcp.device_info().strip_cnt();
152 DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
155 if (_mcp.device_info().uses_ipmidi()) {
156 /* ipMIDI port already exists, we can just assume that we're
159 * If the user still hasn't connected the ipMIDI surface and/or
160 * turned it on, then they have to press "Discover Mackie
161 * Devices" in the GUI at the right time.
164 connection_state |= (InputConnected|OutputConnected);
168 connect_to_signals ();
170 DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
175 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
178 g_source_destroy (input_source);
182 // delete groups (strips)
183 for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
187 // delete controls (global buttons, master fader etc)
188 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
194 // the ports take time to release and we may be rebuilding right away
195 // in the case of changing devices.
197 DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
201 Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
207 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
208 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
210 if (ni == name1 || ni == name2) {
212 connection_state |= InputConnected;
214 connection_state &= ~InputConnected;
216 } else if (no == name1 || no == name2) {
218 connection_state |= OutputConnected;
220 connection_state &= ~OutputConnected;
227 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
229 /* this will send a device query message, which should
230 result in a response that will kick off device type
231 discovery and activation of the surface(s).
233 The intended order of events is:
235 - each surface sends a device query message
236 - devices respond with either MCP or LCP response (sysex in both
238 - sysex message causes Surface::turn_it_on() which tells the
239 MCP object that the surface is ready, and sets up strip
240 displays and binds faders and buttons for that surface
242 In the case of LCP, where this is a handshake process that could
243 fail, the response process to the initial sysex after a device query
244 will mark the surface inactive, which won't shut anything down
245 but will stop any writes to the device.
247 Note: there are no known cases of the handshake process failing.
249 We actually can't initiate this in this callback, so we have
250 to queue it with the MCP event loop.
253 /* XXX this is a horrible hack. Without a short sleep here,
254 something prevents the device wakeup messages from being
255 sent and/or the responses from being received.
262 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
266 return true; /* connection status changed */
272 XMLNode* node = new XMLNode (X_("Surface"));
273 node->set_property (X_("name"), _name);
274 node->add_child_nocopy (_port->get_state());
279 Surface::set_state (const XMLNode& node, int version)
281 /* Look for a node named after the device we're part of */
283 XMLNodeList const& children = node.children();
286 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
288 if ((*c)->get_property (X_("name"), name) && name == _name) {
298 XMLNode* portnode = mynode->child (X_("Port"));
300 if (_port->set_state (*portnode, version)) {
309 Surface::sysex_hdr() const
313 if (_mcp.device_info().is_qcon()) {
314 return mackie_sysex_hdr_qcon;
316 return mackie_sysex_hdr;
319 if(_mcp.device_info().is_qcon()) {
320 return mackie_sysex_hdr_xt_qcon;
322 return mackie_sysex_hdr_xt;
325 cout << "SurfacePort::sysex_hdr _port_type not known" << endl;
326 return mackie_sysex_hdr;
329 static GlobalControlDefinition mackie_global_controls[] = {
330 { "external", Pot::External, Pot::factory, "none" },
331 { "fader_touch", Led::FaderTouch, Led::factory, "master" },
332 { "timecode", Led::Timecode, Led::factory, "none" },
333 { "beats", Led::Beats, Led::factory, "none" },
334 { "solo", Led::RudeSolo, Led::factory, "none" },
335 { "relay_click", Led::RelayClick, Led::factory, "none" },
336 { "", 0, Led::factory, "" }
340 Surface::init_controls()
344 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
345 groups["assignment"] = new Group ("assignment");
346 groups["automation"] = new Group ("automation");
347 groups["bank"] = new Group ("bank");
348 groups["cursor"] = new Group ("cursor");
349 groups["display"] = new Group ("display");
350 groups["function select"] = new Group ("function select");
351 groups["global view"] = new Group ("global view");
352 groups["master"] = new Group ("master");
353 groups["modifiers"] = new Group ("modifiers");
354 groups["none"] = new Group ("none");
355 groups["transport"] = new Group ("transport");
356 groups["user"] = new Group ("user");
357 groups["utilities"] = new Group ("utilities");
359 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
360 if (_mcp.device_info().has_jog_wheel()) {
361 _jog_wheel = new Mackie::JogWheel (_mcp);
364 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
365 for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
366 group = groups[mackie_global_controls[n].group_name];
367 Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
368 controls_by_device_independent_id[mackie_global_controls[n].id] = control;
371 /* add global buttons */
372 DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
373 const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
375 for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
376 group = groups[b->second.group];
377 controls_by_device_independent_id[b->first] = Button::factory (*this, b->first, b->second.id, b->second.label, *group);
382 Surface::init_strips (uint32_t n)
384 const map<Button::ID,StripButtonInfo>& strip_buttons (_mcp.device_info().strip_buttons());
386 for (uint32_t i = 0; i < n; ++i) {
390 snprintf (name, sizeof (name), "strip_%d", (8* _number) + i);
392 Strip* strip = new Strip (*this, name, i, strip_buttons);
394 groups[name] = strip;
395 strips.push_back (strip);
400 Surface::master_monitor_may_have_changed ()
402 if (_number == _mcp.device_info().master_position()) {
408 Surface::setup_master ()
410 boost::shared_ptr<Stripable> m;
412 if ((m = _mcp.get_session().monitor_out()) == 0) {
413 m = _mcp.get_session().master_out();
418 _master_fader->set_control (boost::shared_ptr<AutomationControl>());
420 master_connection.disconnect ();
424 if (!_master_fader) {
425 Groups::iterator group_it;
427 group_it = groups.find("master");
429 if (group_it == groups.end()) {
430 groups["master"] = master_group = new Group ("master");
432 master_group = group_it->second;
435 _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *master_group));
437 DeviceInfo device_info = _mcp.device_info();
438 GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
439 Button* bb = dynamic_cast<Button*> (Button::factory (
441 Button::MasterFaderTouch,
447 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
448 number(), Button::MasterFaderTouch, bb->id()));
450 master_connection.disconnect ();
453 _master_fader->set_control (m->gain_control());
454 m->gain_control()->Changed.connect (master_connection, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
455 _last_master_gain_written = FLT_MAX; /* some essentially impossible value */
456 master_gain_changed ();
460 Surface::master_gain_changed ()
462 if (!_master_fader) {
466 boost::shared_ptr<AutomationControl> ac = _master_fader->control();
471 float normalized_position = ac->internal_to_interface (ac->get_value());
472 if (normalized_position == _last_master_gain_written) {
476 DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
478 _port->write (_master_fader->set_position (normalized_position));
479 _last_master_gain_written = normalized_position;
483 Surface::scaled_delta (float delta, float current_speed)
485 /* XXX needs work before use */
486 const float sign = delta < 0.0 ? -1.0 : 1.0;
488 return ((sign * std::pow (delta + 1.0, 2.0)) + current_speed) / 100.0;
492 Surface::display_bank_start (uint32_t current_bank)
494 if (current_bank == 0) {
495 // send Ar. to 2-char display on the master
496 show_two_char_display ("Ar", "..");
498 // write the current first remote_id to the 2-char display
499 show_two_char_display (current_bank);
504 Surface::blank_jog_ring ()
506 Control* control = controls_by_device_independent_id[Jog::ID];
509 Pot* pot = dynamic_cast<Pot*> (control);
511 _port->write (pot->set (0.0, false, Pot::spread));
517 Surface::scrub_scaling_factor () const
523 Surface::connect_to_signals ()
528 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
529 number(), _port->input_port().name()));
531 MIDI::Parser* p = _port->input_port().parser();
534 p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3));
535 /* V-Pot messages are Controller */
536 p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
537 /* Button messages are NoteOn */
538 p->note_on.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
539 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
540 p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
541 /* Fader messages are Pitchbend */
543 for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
544 p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
547 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()));
554 Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uint32_t fader_id)
556 /* Pitchbend messages are fader position messages. Nothing in the data we get
557 * from the MIDI::Parser conveys the fader ID, which was given by the
558 * channel ID in the status byte.
560 * Instead, we have used bind() to supply the fader-within-strip ID
561 * when we connected to the per-channel pitchbend events.
564 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
565 fader_id, pb, _number, pb/16384.0));
567 if (_mcp.device_info().no_handshake()) {
571 Fader* fader = faders[fader_id];
574 Strip* strip = dynamic_cast<Strip*> (&fader->group());
575 float pos = pb / 16384.0;
577 strip->handle_fader (*fader, pos);
579 DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
581 fader->set_value (pos); // alter master gain
582 _port->write (fader->set_position (pos)); // write back value (required for servo)
585 DEBUG_TRACE (DEBUG::MackieControl, "fader not found\n");
590 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
592 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
594 if (_mcp.device_info().no_handshake()) {
598 if (_mcp.device_info().device_type() == DeviceInfo::HUI && ev->note_number == 0 && ev->velocity == 127) {
602 /* fader touch sense is given by "buttons" 0xe..0xe7 and 0xe8 for the
606 if (ev->note_number >= 0xE0 && ev->note_number <= 0xE8) {
607 Fader* fader = faders[ev->note_number];
609 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface: fader touch message, fader = %1\n", fader));
613 Strip* strip = dynamic_cast<Strip*> (&fader->group());
615 if (ev->velocity > 64) {
616 strip->handle_fader_touch (*fader, true);
618 strip->handle_fader_touch (*fader, false);
624 Button* button = buttons[ev->note_number];
628 if (ev->velocity > 64) {
632 Strip* strip = dynamic_cast<Strip*> (&button->group());
635 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 button %2 pressed ? %3\n",
636 strip->index(), button->name(), (ev->velocity > 64)));
637 strip->handle_button (*button, ev->velocity > 64 ? press : release);
640 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", button->id()));
641 _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
644 if (ev->velocity <= 64) {
649 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
652 /* button release should reset timer AFTER handler(s) have run */
656 Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
658 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_midi_controller %1 = %2\n", (int) ev->controller_number, (int) ev->value));
660 if (_mcp.device_info().no_handshake()) {
664 Pot* pot = pots[ev->controller_number];
666 // bit 6 gives the sign
667 float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
668 // bits 0..5 give the velocity. we interpret this as "ticks
669 // moved before this message was sent"
670 float ticks = (ev->value & 0x3f);
672 /* euphonix and perhaps other devices send zero
673 when they mean 1, we think.
679 if (mcp().main_modifier_state() == MackieControlProtocol::MODIFIER_SHIFT) {
680 delta = sign * (ticks / (float) 0xff);
682 delta = sign * (ticks / (float) 0x3f);
686 if (ev->controller_number == Jog::ID && _jog_wheel) {
688 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", ticks));
689 _jog_wheel->jog_event (delta);
692 // add external (pedal?) control here
697 Strip* strip = dynamic_cast<Strip*> (&pot->group());
699 strip->handle_pot (*pot, delta);
704 Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
706 MidiByteArray bytes (count, raw_bytes);
708 if (_mcp.device_info().no_handshake()) {
712 /* always save the device type ID so that our outgoing sysex messages
717 if (_mcp.device_info().is_qcon()) {
718 mackie_sysex_hdr_qcon[4] = bytes[4];
720 mackie_sysex_hdr[4] = bytes[4];
724 if (_mcp.device_info().is_qcon()) {
725 mackie_sysex_hdr_xt_qcon[4] = bytes[4];
727 mackie_sysex_hdr_xt[4] = bytes[4];
734 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
737 LCP: Connection Challenge
739 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
740 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device connection challenge\n");
741 write_sysex (host_connection_query (bytes));
744 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mackie Control Device ready, current status = %1\n", _active));
752 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
754 /* Behringer X-Touch Compact: Device Ready
756 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Behringer X-Touch Compact ready, current status = %1\n", _active));
760 case 0x03: /* LCP Connection Confirmation */
761 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
762 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device confirms connection, ardour replies\n");
763 if (bytes[4] == 0x10 || bytes[4] == 0x11) {
764 write_sysex (host_connection_confirmation (bytes));
769 case 0x04: /* LCP: Confirmation Denied */
770 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
771 DEBUG_TRACE (DEBUG::MackieControl, "Logic Control Device denies connection\n");
776 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
777 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("unknown device ID byte %1", (int) bytes[5]));
778 error << "MCP: unknown sysex: " << bytes << endmsg;
783 calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end)
786 back_insert_iterator<MidiByteArray> back (l);
787 copy (begin, end, back);
789 MidiByteArray retval;
791 // this is how to calculate the response to the challenge.
792 // from the Logic docs.
793 retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3]));
794 retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3])));
795 retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1])));
796 retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4))));
802 Surface::host_connection_query (MidiByteArray & bytes)
804 MidiByteArray response;
806 if (bytes[4] != 0x10 && bytes[4] != 0x11) {
807 /* not a Logic Control device - no response required */
811 // handle host connection query
812 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
814 if (bytes.size() != 18) {
815 cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl;
819 // build and send host connection reply
821 copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response));
822 response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4);
827 Surface::host_connection_confirmation (const MidiByteArray & bytes)
829 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
831 // decode host connection confirmation
832 if (bytes.size() != 14) {
834 os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name();
835 throw MackieControlException (os.str());
838 // send version request
839 return MidiByteArray (2, 0x13, 0x00);
843 Surface::turn_it_on ()
851 _mcp.device_ready ();
853 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
857 update_view_mode_display (false);
859 // if (_mcp.device_info ().has_global_controls ()) {
860 // _mcp.update_global_button (Button::Read, _mcp.metering_active ());
865 Surface::write_sysex (const MidiByteArray & mba)
872 buf << sysex_hdr() << mba << MIDI::eox;
877 Surface::write_sysex (MIDI::byte msg)
880 buf << sysex_hdr() << msg << MIDI::eox;
885 Surface::n_strips (bool with_locked_strips) const
887 if (with_locked_strips) {
888 return strips.size();
893 for (Strips::const_iterator it = strips.begin(); it != strips.end(); ++it) {
894 if (!(*it)->locked()) {
902 Surface::nth_strip (uint32_t n) const
904 if (n > n_strips()) {
913 if (_mcp.device_info().has_timecode_display ()) {
914 display_timecode (string (10, '0'), string (10, ' '));
917 if (_mcp.device_info().has_two_character_display()) {
918 show_two_char_display (string (2, '0'), string (2, ' '));
921 if (_mcp.device_info().has_master_fader () && _master_fader) {
922 _port->write (_master_fader->zero ());
926 for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
934 Surface::zero_controls ()
936 if (!_mcp.device_info().has_global_controls()) {
940 // turn off global buttons and leds
942 for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
943 Control & control = **it;
944 if (!control.group().is_strip()) {
945 _port->write (control.zero());
949 // and the led ring for the master strip
952 _last_master_gain_written = 0.0f;
956 Surface::periodic (uint64_t now_usecs)
958 master_gain_changed();
959 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
960 (*s)->periodic (now_usecs);
965 Surface::redisplay (ARDOUR::microseconds_t now, bool force)
967 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
968 (*s)->redisplay (now, force);
973 Surface::write (const MidiByteArray& data)
978 DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
983 Surface::update_strip_selection ()
985 Strips::iterator s = strips.begin();
986 for ( ; s != strips.end(); ++s) {
987 (*s)->update_selection_state();
992 Surface::map_stripables (const vector<boost::shared_ptr<Stripable> >& stripables)
994 vector<boost::shared_ptr<Stripable> >::const_iterator r;
995 Strips::iterator s = strips.begin();
997 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Mapping %1 stripables to %2 strips\n", stripables.size(), strips.size()));
999 for (r = stripables.begin(); r != stripables.end() && s != strips.end(); ++s) {
1001 /* don't try to assign stripables to a locked strip. it won't
1002 use it anyway, but if we do, then we get out of sync
1003 with the proposed mapping.
1006 if (!(*s)->locked()) {
1007 (*s)->set_stripable (*r);
1012 for (; s != strips.end(); ++s) {
1013 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 being set to null stripable\n", (*s)->index()));
1014 (*s)->set_stripable (boost::shared_ptr<Stripable>());
1019 translate_seven_segment (char achar)
1021 achar = toupper (achar);
1023 if (achar >= 0x40 && achar <= 0x60) {
1024 return achar - 0x40;
1025 } else if (achar >= 0x21 && achar <= 0x3f) {
1033 Surface::show_two_char_display (const std::string & msg, const std::string & dots)
1035 if (_stype != mcu || !_mcp.device_info().has_two_character_display() || msg.length() != 2 || dots.length() != 2) {
1039 MidiByteArray right (3, 0xb0, 0x4b, 0x00);
1040 MidiByteArray left (3, 0xb0, 0x4a, 0x00);
1042 right[2] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
1043 left[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
1045 _port->write (right);
1046 _port->write (left);
1050 Surface::show_two_char_display (unsigned int value, const std::string & /*dots*/)
1053 os << setfill('0') << setw(2) << value % 100;
1054 show_two_char_display (os.str());
1058 Surface::display_timecode (const std::string & timecode, const std::string & last_timecode)
1060 //TODO: Fix for Qcon to correct timecode value if is over 1000 bars
1062 if (!_active || !_mcp.device_info().has_timecode_display()) {
1065 // if there's no change, send nothing, not even sysex header
1066 if (timecode == last_timecode) return;
1068 // length sanity checking
1069 string local_timecode = timecode;
1071 // truncate to 10 characters
1072 if (local_timecode.length() > 10) {
1073 local_timecode = local_timecode.substr (0, 10);
1076 // pad to 10 characters
1077 while (local_timecode.length() < 10) {
1078 local_timecode += " ";
1081 // translate characters.
1082 // Only the characters that actually changed are sent.
1083 int position = 0x3f;
1085 for (i = local_timecode.length () - 1; i >= 0; i--) {
1087 if (local_timecode[i] == last_timecode[i]) {
1090 MidiByteArray retval (2, 0xb0, position);
1091 retval << translate_seven_segment (local_timecode[i]);
1092 _port->write (retval);
1097 Surface::update_flip_mode_display ()
1099 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1100 (*s)->flip_mode_changed ();
1105 Surface::subview_mode_changed ()
1107 for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
1108 (*s)->subview_mode_changed ();
1113 Surface::update_view_mode_display (bool with_helpful_text)
1122 switch (_mcp.view_mode()) {
1123 case MackieControlProtocol::Mixer:
1124 show_two_char_display ("Mx");
1126 text = _("Mixer View");
1128 case MackieControlProtocol::AudioTracks:
1129 show_two_char_display ("AT");
1130 id = Button::AudioTracks;
1131 text = _("Audio Tracks");
1133 case MackieControlProtocol::MidiTracks:
1134 show_two_char_display ("MT");
1135 id = Button::MidiTracks;
1136 text = _("MIDI Tracks");
1138 case MackieControlProtocol::Plugins:
1139 show_two_char_display ("PL");
1140 id = Button::Plugin;
1141 text = _("Plugins");
1143 case MackieControlProtocol::Busses:
1144 show_two_char_display ("BS");
1145 id = Button::Busses;
1146 if (Profile->get_mixbus()) {
1147 text = _("Mixbusses");
1152 case MackieControlProtocol::Auxes:
1153 show_two_char_display ("Au");
1157 case MackieControlProtocol::Hidden:
1158 show_two_char_display ("HI");
1159 id = Button::Outputs;
1160 text = _("Hidden Tracks");
1162 case MackieControlProtocol::Selected:
1163 show_two_char_display ("SE");
1165 text = _("Selected Tracks");
1171 vector<int> view_mode_buttons;
1172 view_mode_buttons.push_back (Button::View);
1173 view_mode_buttons.push_back (Button::Busses);
1174 view_mode_buttons.push_back (Button::Plugin);
1175 view_mode_buttons.push_back (Button::AudioTracks);
1176 view_mode_buttons.push_back (Button::MidiTracks);
1177 view_mode_buttons.push_back (Button::Aux);
1178 view_mode_buttons.push_back (Button::Outputs);
1179 view_mode_buttons.push_back (Button::User);
1183 for (vector<int>::iterator i = view_mode_buttons.begin(); i != view_mode_buttons.end(); ++i) {
1184 map<int,Control*>::iterator x = controls_by_device_independent_id.find (*i);
1186 if (x != controls_by_device_independent_id.end()) {
1187 Button* button = dynamic_cast<Button*> (x->second);
1192 _port->write (button->set_state (onoff));
1198 if (with_helpful_text && !text.empty()) {
1199 display_message_for (text, 1000);
1204 Surface::say_hello ()
1206 /* wakeup for Mackie Control */
1207 MidiByteArray wakeup (7, MIDI::sysex, 0x00, 0x00, 0x66, 0x14, 0x00, MIDI::eox);
1208 _port->write (wakeup);
1209 wakeup[4] = 0x15; /* wakup Mackie XT */
1210 _port->write (wakeup);
1211 wakeup[4] = 0x10; /* wakeup Logic Control */
1212 _port->write (wakeup);
1213 wakeup[4] = 0x11; /* wakeup Logic Control XT */
1214 _port->write (wakeup);
1218 Surface::next_jog_mode ()
1223 Surface::set_jog_mode (JogWheel::Mode)
1228 Surface::stripable_is_locked_to_strip (boost::shared_ptr<Stripable> stripable) const
1230 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1231 if ((*s)->stripable() == stripable && (*s)->locked()) {
1239 Surface::stripable_is_mapped (boost::shared_ptr<Stripable> stripable) const
1241 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1242 if ((*s)->stripable() == stripable) {
1251 Surface::notify_metering_state_changed()
1253 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1254 (*s)->notify_metering_state_changed ();
1262 /* reset msg for Mackie Control */
1273 Surface::toggle_backlight ()
1276 int onoff = random() %2;
1278 msg << sysex_hdr ();
1280 msg << (onoff ? 0x1 : 0x0);
1287 Surface::recalibrate_faders ()
1291 msg << sysex_hdr ();
1300 Surface::set_touch_sensitivity (int sensitivity)
1302 /* NOTE: assumed called from GUI code, hence sleep() */
1304 /* sensitivity already clamped by caller */
1306 if( !is_qcon ) { // Qcon doesn't support fader sensitivity
1310 msg << sysex_hdr ();
1312 msg << 0xff; /* overwritten for each fader below */
1313 msg << (sensitivity & 0x7f);
1316 for (int fader = 0; fader < 9; ++fader) {
1325 Surface::hui_heartbeat ()
1331 MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
1336 Surface::connected ()
1338 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
1342 if (_mcp.device_info().no_handshake()) {
1348 Surface::display_line (string const& msg, int line_num)
1350 MidiByteArray midi_msg;
1351 midi_msg << sysex_hdr ();
1353 midi_msg << (line_num ? 0x38 : 0x0); /* offsets into char array
1355 * correspond to line
1360 midi_msg.insert (midi_msg.end(), 55, ' ');
1364 /* ascii data to display. @param msg is UTF-8 which is not legal. */
1365 string ascii = Glib::convert_with_fallback (msg, "UTF-8", "ISO-8859-1", "_");
1366 string::size_type len = ascii.length();
1369 midi_msg << ascii.substr (0, 55);
1373 for (string::size_type i = len; i < 55; ++i) {
1379 midi_msg << MIDI::eox;
1384 /** display @param msg on the 55x2 screen for @param msecs milliseconds
1386 * @param msg is assumed to be UTF-8 encoded, and will be converted
1387 * to ASCII with an underscore as fallback character before being
1388 * sent to the device.
1391 Surface::display_message_for (string const& msg, uint64_t msecs)
1393 string::size_type newline;
1395 if ((newline = msg.find ('\n')) == string::npos) {
1397 _port->write (display_line (msg, 0));
1398 _port->write (display_line (string(), 1));
1400 } else if (newline == 0) {
1402 _port->write (display_line (string(), 0));
1403 _port->write (display_line (msg.substr (1), 1));
1407 string first_line = msg.substr (0, newline-1);
1408 string second_line = msg.substr (newline+1);
1410 _port->write (display_line (first_line, 0));
1411 _port->write (display_line (second_line.substr (0, second_line.find_first_of ('\n')), 1));
1414 for (Strips::const_iterator s = strips.begin(); s != strips.end(); ++s) {
1415 (*s)->block_screen_display_for (msecs);