LCXL: split track focus button and led code
[ardour.git] / libs / surfaces / launch_control_xl / launch_control_xl.cc
1 /*
2   Copyright (C) 2016 Paul Davis
3
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.
8
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.
13
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.
17 */
18
19 #include <stdlib.h>
20 #include <pthread.h>
21
22 #include "pbd/compose.h"
23 #include "pbd/convert.h"
24 #include "pbd/debug.h"
25 #include "pbd/failed_constructor.h"
26 #include "pbd/file_utils.h"
27 #include "pbd/search_path.h"
28 #include "pbd/enumwriter.h"
29
30 #include "midi++/parser.h"
31
32 #include "ardour/amp.h"
33 #include "ardour/async_midi_port.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/debug.h"
36 #include "ardour/midiport_manager.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/midi_port.h"
39 #include "ardour/session.h"
40 #include "ardour/tempo.h"
41 #include "ardour/types_convert.h"
42 #include "ardour/vca_manager.h"
43
44
45 #include "gtkmm2ext/gui_thread.h"
46
47 #include "gui.h"
48 #include "launch_control_xl.h"
49
50 #include "pbd/i18n.h"
51
52 #ifdef PLATFORM_WINDOWS
53 #define random() rand()
54 #endif
55
56 using namespace ARDOUR;
57 using namespace std;
58 using namespace PBD;
59 using namespace Glib;
60 using namespace ArdourSurface;
61 #include "pbd/abstract_ui.cc" // instantiate template
62
63 /* init global object */
64 LaunchControlXL* lcxl = 0;
65
66 LaunchControlXL::LaunchControlXL (ARDOUR::Session& s)
67         : ControlProtocol (s, string (X_("Novation Launch Control XL")))
68         , AbstractUI<LaunchControlRequest> (name())
69         , in_use (false)
70         , _track_mode(TrackMute)
71         , _template_number(8) // default template (factory 1)
72         , bank_start (0)
73         , connection_state (ConnectionState (0))
74         , gui (0)
75         , in_range_select (false)
76 {
77         lcxl = this;
78         /* we're going to need this */
79
80         build_maps ();
81
82         /* master cannot be removed, so no need to connect to going-away signal */
83         master = session->master_out ();
84         /* the master bus will always be on the last channel on the lcxl */
85         stripable[7] = master;
86
87
88         run_event_loop ();
89
90         /* Ports exist for the life of this instance */
91
92         ports_acquire ();
93
94         /* catch arrival and departure of LaunchControlXL itself */
95         ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::port_registration_handler, this), this);
96
97         /* Catch port connections and disconnections */
98         ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::connection_handler, this, _1, _2, _3, _4, _5), this);
99
100         /* Launch Control XL ports might already be there */
101         port_registration_handler ();
102
103         session->RouteAdded.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::stripables_added, this), lcxl);
104         session->vca_manager().VCAAdded.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::stripables_added, this), lcxl);
105
106         switch_bank (bank_start);
107 }
108
109 LaunchControlXL::~LaunchControlXL ()
110 {
111         DEBUG_TRACE (DEBUG::LaunchControlXL, "Launch Control XL  control surface object being destroyed\n");
112
113         /* do this before stopping the event loop, so that we don't get any notifications */
114         port_reg_connection.disconnect ();
115         port_connection.disconnect ();
116         session_connections.drop_connections ();
117         stripable_connections.drop_connections ();
118
119         stop_using_device ();
120         ports_release ();
121
122         stop_event_loop ();
123 }
124
125
126 void
127 LaunchControlXL::run_event_loop ()
128 {
129         DEBUG_TRACE (DEBUG::LaunchControlXL, "start event loop\n");
130         BaseUI::run ();
131 }
132
133 void
134 LaunchControlXL::stop_event_loop ()
135 {
136         DEBUG_TRACE (DEBUG::LaunchControlXL, "stop event loop\n");
137         BaseUI::quit ();
138 }
139
140 int
141 LaunchControlXL::begin_using_device ()
142 {
143         DEBUG_TRACE (DEBUG::LaunchControlXL, "begin using device\n");
144
145         switch_template(template_number()); // first factory template
146
147         connect_session_signals ();
148
149         init_buttons (true);
150
151         in_use = true;
152
153         return 0;
154 }
155
156 int
157 LaunchControlXL::stop_using_device ()
158 {
159         DEBUG_TRACE (DEBUG::LaunchControlXL, "stop using device\n");
160
161         if (!in_use) {
162                 DEBUG_TRACE (DEBUG::LaunchControlXL, "nothing to do, device not in use\n");
163                 return 0;
164         }
165
166         init_buttons (false);
167
168         session_connections.drop_connections ();
169
170         in_use = false;
171         return 0;
172 }
173
174 int
175 LaunchControlXL::ports_acquire ()
176 {
177         DEBUG_TRACE (DEBUG::LaunchControlXL, "acquiring ports\n");
178
179         /* setup ports */
180
181         _async_in  = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Launch Control XL in"), true);
182         _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Launch Control XL out"), true);
183
184         if (_async_in == 0 || _async_out == 0) {
185                 DEBUG_TRACE (DEBUG::LaunchControlXL, "cannot register ports\n");
186                 return -1;
187         }
188
189         /* We do not add our ports to the input/output bundles because we don't
190          * want users wiring them by hand. They could use JACK tools if they
191          * really insist on that (and use JACK)
192          */
193
194         _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
195         _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
196
197         session->BundleAddedOrRemoved ();
198
199         connect_to_parser ();
200
201         /* Connect input port to event loop */
202
203         AsyncMIDIPort* asp;
204
205         asp = static_cast<AsyncMIDIPort*> (_input_port);
206         asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &LaunchControlXL::midi_input_handler), _input_port));
207         asp->xthread().attach (main_loop()->get_context());
208
209         return 0;
210 }
211
212 void
213 LaunchControlXL::ports_release ()
214 {
215         DEBUG_TRACE (DEBUG::LaunchControlXL, "releasing ports\n");
216
217         /* wait for button data to be flushed */
218         AsyncMIDIPort* asp;
219         asp = static_cast<AsyncMIDIPort*> (_output_port);
220         asp->drain (10000, 500000);
221
222         {
223                 Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
224                 AudioEngine::instance()->unregister_port (_async_in);
225                 AudioEngine::instance()->unregister_port (_async_out);
226         }
227
228         _async_in.reset ((ARDOUR::Port*) 0);
229         _async_out.reset ((ARDOUR::Port*) 0);
230         _input_port = 0;
231         _output_port = 0;
232 }
233
234 list<boost::shared_ptr<ARDOUR::Bundle> >
235 LaunchControlXL::bundles ()
236 {
237         list<boost::shared_ptr<ARDOUR::Bundle> > b;
238
239         if (_output_bundle) {
240                 b.push_back (_output_bundle);
241         }
242
243         return b;
244 }
245
246
247 void
248 LaunchControlXL::init_buttons (bool startup)
249 {
250         reset(template_number());
251
252         if (startup) {
253                 switch_bank(bank_start);
254         }
255 }
256
257 bool
258 LaunchControlXL::probe ()
259 {
260         return true;
261 }
262
263 void*
264 LaunchControlXL::request_factory (uint32_t num_requests)
265 {
266         /* AbstractUI<T>::request_buffer_factory() is a template method only
267            instantiated in this source module. To provide something visible for
268            use in the interface/descriptor, we have this static method that is
269            template-free.
270         */
271         return request_buffer_factory (num_requests);
272 }
273
274 void
275 LaunchControlXL::do_request (LaunchControlRequest * req)
276 {
277         if (req->type == CallSlot) {
278
279                 call_slot (MISSING_INVALIDATOR, req->the_slot);
280
281         } else if (req->type == Quit) {
282
283                 stop_using_device ();
284         }
285 }
286
287 void
288 LaunchControlXL::reset(uint8_t chan)
289 {
290         MidiByteArray msg (3, 176 + chan, 0, 0); // turn off all leds, reset buffer settings and duty cycle
291
292         write(msg);
293 }
294 int
295 LaunchControlXL::set_active (bool yn)
296 {
297         DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose("LaunchControlProtocol::set_active init with yn: '%1'\n", yn));
298
299         if (yn == active()) {
300                 return 0;
301         }
302
303         if (yn) {
304                 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
305                         begin_using_device ();
306                 } else {
307                         /* begin_using_device () will get called once we're connected */
308                 }
309
310         } else {
311                 /* Control Protocol Manager never calls us with false, but
312                  * insteads destroys us.
313                  */
314         }
315
316         ControlProtocol::set_active (yn);
317
318         DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose("LaunchControlProtocol::set_active done with yn: '%1'\n", yn));
319
320         return 0;
321 }
322
323 void
324 LaunchControlXL::write (const MidiByteArray& data)
325 {
326         /* immediate delivery */
327         _output_port->write (&data[0], data.size(), 0);
328 }
329
330 /* Device to Ardour message handling */
331
332 bool
333 LaunchControlXL::midi_input_handler (IOCondition ioc, MIDI::Port* port)
334 {
335         if (ioc & ~IO_IN) {
336                 DEBUG_TRACE (DEBUG::LaunchControlXL, "MIDI port closed\n");
337                 return false;
338         }
339
340         if (ioc & IO_IN) {
341
342                 DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("something happened on  %1\n", port->name()));
343
344                 AsyncMIDIPort* asp = static_cast<AsyncMIDIPort*>(port);
345                 if (asp) {
346                         asp->clear ();
347                 }
348
349                 DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("data available on %1\n", port->name()));
350                 if (in_use) {
351                         samplepos_t now = AudioEngine::instance()->sample_time();
352                         port->parse (now);
353                 }
354         }
355
356         return true;
357 }
358
359
360 void
361 LaunchControlXL::connect_to_parser ()
362 {
363         DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Connecting to signals on port %1\n", _input_port->name()));
364
365         MIDI::Parser* p = _input_port->parser();
366
367         /* Incoming sysex */
368         p->sysex.connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_sysex, this, _1, _2, _3));
369
370  for (MIDI::channel_t n = 0; n < 16; ++n) {
371         /* Controller */
372                 p->channel_controller[(int)n].connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_controller_message, this, _1, _2, n));
373                 /* Button messages are NoteOn */
374                 p->channel_note_on[(int)n].connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_note_on_message, this, _1, _2, n));
375                 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
376                 p->channel_note_off[(int)n].connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_note_off_message, this, _1, _2, n));
377         }
378 }
379
380 void
381 LaunchControlXL::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
382 {
383         DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Sysex, %1 bytes\n", sz));
384
385         if (sz < 8) {
386                 return;
387         }
388
389         MidiByteArray msg (sz, raw_bytes);
390         MidiByteArray lcxl_sysex_header (6, 0xF0, 0x00, 0x20, 0x29, 0x02, 0x11);
391
392         if (!lcxl_sysex_header.compare_n (msg, 6)) {
393                 return;
394         }
395
396
397         switch (msg[6]) {
398         case 0x77: /* template change */
399                 DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Template change: %1 n", msg[7]));
400                 _template_number = msg[7];
401                 break;
402         }
403 }
404
405
406 void
407 LaunchControlXL::handle_button_message(Button* button, MIDI::EventTwoBytes* ev)
408 {
409   if (ev->value) {
410     /* any press cancels any pending long press timeouts */
411     for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
412       ControllerButton* cb = id_controller_button_map[*x];
413                         NoteButton*     nb = id_note_button_map[*x];
414                         if (cb != 0) {
415                                 cb->timeout_connection.disconnect();
416                         } else if (nb != 0) {
417                                         nb->timeout_connection.disconnect();
418                         }
419     }
420
421     buttons_down.insert(button->id());
422     DEBUG_TRACE(DEBUG::LaunchControlXL, string_compose("button pressed: %1\n", LaunchControlXL::button_name_by_id(button->id())));
423     start_press_timeout(button, button->id());
424   } else {
425     DEBUG_TRACE(DEBUG::LaunchControlXL, string_compose("button depressed: %1\n", LaunchControlXL::button_name_by_id(button->id())));
426     buttons_down.erase(button->id());
427     button->timeout_connection.disconnect();
428   }
429
430         set<ButtonID>::iterator c = consumed.find(button->id());
431
432   if (c == consumed.end()) {
433     if (ev->value == 0) {
434       (this->*button->release_method)();
435     } else {
436       (this->*button->press_method)();
437     }
438   } else {
439     DEBUG_TRACE(DEBUG::LaunchControlXL, "button was consumed, ignored\n");
440     consumed.erase(c);
441   }
442 }
443
444 bool
445 LaunchControlXL::check_pick_up(Controller* controller, boost::shared_ptr<AutomationControl> ac)
446 {
447         /* returns false until the controller value matches with the current setting of the stripable's ac */
448         return ( abs( controller->value() / 127.0 -  ac->internal_to_interface(ac->get_value()) ) < 0.007875 );
449 }
450
451 void
452 LaunchControlXL::handle_knob_message (Knob* knob)
453 {
454         uint8_t chan = knob->id() % 8; // get the strip channel number
455         if (!stripable[chan]) {
456                 return;
457         }
458
459         boost::shared_ptr<AutomationControl> ac;
460
461         if (knob->id() < 8) { // sendA knob
462                 if (buttons_down.find(Device) != buttons_down.end()) { // Device button hold
463                         ac = stripable[chan]->trim_control();
464                 } else {
465                         ac = stripable[chan]->send_level_controllable (0);
466                 }
467         } else if (knob->id() >= 8 && knob->id() < 16) { // sendB knob
468                 if (buttons_down.find(Device) != buttons_down.end()) { // Device button hold
469                         /* something */
470                 } else {
471                         ac = stripable[chan]->send_level_controllable (1);
472                 }
473         } else if (knob->id() >= 16 && knob->id() < 24) { // pan knob
474                 if (buttons_down.find(Device) != buttons_down.end()) { // Device button hold
475                         ac = stripable[chan]->pan_width_control();
476                 } else {
477                         ac = stripable[chan]->pan_azimuth_control();
478                 }
479         }
480
481         if (ac && check_pick_up(knob, ac)) {
482                 ac->set_value ( ac->interface_to_internal( knob->value() / 127.0), PBD::Controllable::UseGroup );
483         }
484 }
485
486 void
487 LaunchControlXL::handle_fader_message (Fader* fader)
488 {
489
490         if (!stripable[fader->id()]) {
491                 return;
492         }
493
494         boost::shared_ptr<AutomationControl> ac = stripable[fader->id()]->gain_control();
495         if (ac && check_pick_up(fader, ac)) {
496                 ac->set_value ( ac->interface_to_internal( fader->value() / 127.0), PBD::Controllable::UseGroup );
497         }
498 }
499
500 void
501 LaunchControlXL::handle_midi_controller_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev, MIDI::channel_t chan)
502 {
503         _template_number = (int)chan;
504
505         if (template_number() < 8) {
506                 return; // only treat factory templates
507         }
508         // DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
509
510         CCControllerButtonMap::iterator b = cc_controller_button_map.find (ev->controller_number);
511         CCFaderMap::iterator f = cc_fader_map.find (ev->controller_number);
512         CCKnobMap::iterator k = cc_knob_map.find (ev->controller_number);
513
514         if (b != cc_controller_button_map.end()) {
515                 Button* button = b->second;
516                 handle_button_message(button, ev);
517         } else if (f != cc_fader_map.end()) {
518                 Fader* fader = f->second;
519                 fader->set_value(ev->value);
520                 handle_fader_message(fader);
521
522         } else if (k != cc_knob_map.end()) {
523                 Knob* knob = k->second;
524                 knob->set_value(ev->value);
525                 handle_knob_message(knob);
526         }
527 }
528
529 void
530 LaunchControlXL::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev, MIDI::channel_t chan)
531 {
532         _template_number = (int)chan;
533
534         if (template_number() < 8) {
535                 return; // only treat factory templates
536         }
537
538          //DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
539
540          NNNoteButtonMap::iterator b = nn_note_button_map.find (ev->controller_number);
541
542          if (b != nn_note_button_map.end()) {
543                 Button* button = b->second;
544                 handle_button_message(button, ev);
545         }
546 }
547
548 void LaunchControlXL::handle_midi_note_off_message(MIDI::Parser & parser, MIDI::EventTwoBytes *ev, MIDI::channel_t chan)
549 {
550   //DEBUG_TRACE(DEBUG::LaunchControlXL, string_compose("Note Off %1 (velocity %2)\n",(int)ev->note_number, (int)ev->velocity));
551         handle_midi_note_on_message(parser, ev, chan); /* we handle both case in handle_midi_note_on_message */
552 }
553
554 /* Ardour session signals connection */
555
556 void
557 LaunchControlXL::thread_init ()
558 {
559         pthread_set_name (event_loop_name().c_str());
560
561         PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
562         ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
563
564         set_thread_priority ();
565 }
566
567 void
568 LaunchControlXL::connect_session_signals()
569 {
570         // receive transport state changed
571         session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_transport_state_changed, this), this);
572         session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_loop_state_changed, this), this);
573         // receive punch-in and punch-out
574         Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_parameter_changed, this, _1), this);
575         session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_parameter_changed, this, _1), this);
576
577         // receive rude solo changed
578         //session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_solo_active_changed, this, _1), this);
579         // receive record state toggled
580         //session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_record_state_changed, this), this);
581
582 }
583
584
585 void
586 LaunchControlXL::notify_transport_state_changed ()
587 { /*
588         Button* b = id_button_map[Play];
589
590         if (session->transport_rolling()) {
591                 b->set_state (LED::OneShot24th);
592                 b->set_color (LED::GreenFull);
593         } else {
594
595                  disable any blink on FixedLength from pending edit range op
596                 Button* fl = id_button_map[FixedLength];
597
598                 fl->set_color (LED::Black);
599                 fl->set_state (LED::NoTransition);
600                 write (fl->state_msg());
601
602                 b->set_color (LED::White);
603                 b->set_state (LED::NoTransition);
604         }
605
606         write (b->state_msg()); */
607 }
608
609 void
610 LaunchControlXL::notify_loop_state_changed ()
611 {
612 }
613
614 void
615 LaunchControlXL::notify_parameter_changed (std::string param)
616 { /*
617         IDButtonMap::iterator b;
618
619         if (param == "clicking") {
620                 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
621                         return;
622                 }
623                 if (Config->get_clicking()) {
624                         b->second->set_state (LED::Blinking4th);
625                         b->second->set_color (LED::White);
626                 } else {
627                         b->second->set_color (LED::White);
628                         b->second->set_state (LED::NoTransition);
629                 }
630                 write (b->second->state_msg ()) ;
631         } */
632 }
633
634 /* connection handling */
635
636 XMLNode&
637 LaunchControlXL::get_state()
638 {
639         XMLNode& node (ControlProtocol::get_state());
640         XMLNode* child;
641
642         child = new XMLNode (X_("Input"));
643         child->add_child_nocopy (_async_in->get_state());
644         node.add_child_nocopy (*child);
645         child = new XMLNode (X_("Output"));
646         child->add_child_nocopy (_async_out->get_state());
647         node.add_child_nocopy (*child);
648
649         return node;
650 }
651
652 int
653 LaunchControlXL::set_state (const XMLNode & node, int version)
654 {
655         DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("LaunchControlXL::set_state: active %1\n", active()));
656
657         int retval = 0;
658
659         if (ControlProtocol::set_state (node, version)) {
660                 return -1;
661         }
662
663         XMLNode* child;
664
665         if ((child = node.child (X_("Input"))) != 0) {
666                 XMLNode* portnode = child->child (Port::state_node_name.c_str());
667                 if (portnode) {
668                         _async_in->set_state (*portnode, version);
669                 }
670         }
671
672         if ((child = node.child (X_("Output"))) != 0) {
673                 XMLNode* portnode = child->child (Port::state_node_name.c_str());
674                 if (portnode) {
675                         _async_out->set_state (*portnode, version);
676                 }
677         }
678
679         return retval;
680 }
681
682 void
683 LaunchControlXL::port_registration_handler ()
684 {
685         if (!_async_in && !_async_out) {
686                 /* ports not registered yet */
687                 return;
688         }
689
690         if (_async_in->connected() && _async_out->connected()) {
691                 /* don't waste cycles here */
692                 return;
693         }
694
695 #ifdef __APPLE__
696         /* the origin of the numeric magic identifiers is known only to Ableton
697            and may change in time. This is part of how CoreMIDI works.
698         */
699         string input_port_name = X_("system:midi_capture_1319078870");
700         string output_port_name = X_("system:midi_playback_3409210341");
701 #else
702         string input_port_name = X_("Novation Launch Control XL MIDI 1 in");
703         string output_port_name = X_("Novation Launch Control XL MIDI 1 out");
704 #endif
705         vector<string> in;
706         vector<string> out;
707
708         AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
709         AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
710
711         if (!in.empty() && !out.empty()) {
712                 cerr << "LaunchControlXL: both ports found\n";
713                 cerr << "\tconnecting to " << in.front() <<  " + " << out.front() << endl;
714                 if (!_async_in->connected()) {
715                         AudioEngine::instance()->connect (_async_in->name(), in.front());
716                 }
717                 if (!_async_out->connected()) {
718                         AudioEngine::instance()->connect (_async_out->name(), out.front());
719                 }
720         }
721 }
722
723 bool
724 LaunchControlXL::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
725 {
726         DEBUG_TRACE (DEBUG::LaunchControlXL, "LaunchControlXL::connection_handler start\n");
727         if (!_input_port || !_output_port) {
728                 return false;
729         }
730
731         string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
732         string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
733
734         if (ni == name1 || ni == name2) {
735                 if (yn) {
736                         connection_state |= InputConnected;
737                 } else {
738                         connection_state &= ~InputConnected;
739                 }
740         } else if (no == name1 || no == name2) {
741                 if (yn) {
742                         connection_state |= OutputConnected;
743                 } else {
744                         connection_state &= ~OutputConnected;
745                 }
746         } else {
747                 DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
748                 // not our ports
749                 return false;
750         }
751
752         DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
753                                                    name1, name2, yn));
754
755         if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
756
757                 /* XXX this is a horrible hack. Without a short sleep here,
758                    something prevents the device wakeup messages from being
759                    sent and/or the responses from being received.
760                 */
761
762                 g_usleep (100000);
763                 DEBUG_TRACE (DEBUG::LaunchControlXL, "device now connected for both input and output\n");
764
765                 begin_using_device ();
766
767         } else {
768                 DEBUG_TRACE (DEBUG::LaunchControlXL, "Device disconnected (input or output or both) or not yet fully connected\n");
769                 stop_using_device ();
770         }
771
772         ConnectionChange (); /* emit signal for our GUI */
773
774         DEBUG_TRACE (DEBUG::LaunchControlXL, "LaunchControlXL::connection_handler  end\n");
775
776         return true; /* connection status changed */
777 }
778
779
780 boost::shared_ptr<Port>
781 LaunchControlXL::output_port()
782 {
783         return _async_out;
784 }
785
786 boost::shared_ptr<Port>
787 LaunchControlXL::input_port()
788 {
789         return _async_in;
790 }
791
792 /* Stripables handling */
793
794 void
795 LaunchControlXL::stripable_selection_changed () // we don't need it but it's needs to be declared...
796 {
797 }
798
799
800 void
801 LaunchControlXL::stripable_property_change (PropertyChange const& what_changed, uint32_t which)
802 {
803
804         if (what_changed.contains (Properties::hidden)) {
805                 switch_bank (bank_start);
806         }
807
808         if (what_changed.contains (Properties::selected)) {
809
810                 if (!stripable[which]) {
811                         return;
812                 }
813                 if (which < 8) {
814                         update_track_focus_led ((uint8_t) which);
815                 }
816         }
817 }
818
819 void
820 LaunchControlXL::switch_template (uint8_t t)
821 {
822         MidiByteArray msg (9, 0xf0, 0x00, 0x20, 0x29, 0x02, 0x11, 0x77, t, 0xf7);
823         write (msg);
824 }
825
826 void
827 LaunchControlXL::switch_bank (uint32_t base)
828 {
829         SelectButton* sl = static_cast<SelectButton*>(id_controller_button_map[SelectLeft]);
830         SelectButton* sr = static_cast<SelectButton*>(id_controller_button_map[SelectRight]);
831
832         /* work backwards so we can tell if we should actually switch banks */
833
834         boost::shared_ptr<Stripable> s[8];
835         uint32_t different = 0;
836
837         for (int n = 0; n < 7; ++n) {
838                 s[n] = session->get_remote_nth_stripable (base+n, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
839                 if (s[n] != stripable[n]) {
840                         different++;
841                 }
842         }
843
844         if (sl && sr) {
845                 write(sl->state_msg((base)));
846                 write(sr->state_msg((s[1] != 0)));
847         }
848
849         if (!s[0]) {
850                 /* not even the first stripable exists, do nothing */
851                 return;
852         }
853
854         stripable_connections.drop_connections ();
855
856         for (int n = 0; n < 7; ++n) {
857                 stripable[n] = s[n];
858         }
859
860         /* at least one stripable in this bank */
861
862         bank_start = base;
863
864         for (int n = 0; n < 8; ++n) {
865
866                 if (stripable[n]) {
867                         /* stripable goes away? refill the bank, starting at the same point */
868
869                         stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::switch_bank, this, bank_start), lcxl);
870                         stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::stripable_property_change, this, _1, n), lcxl);
871                         stripable[n]->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::solo_changed, this, n), lcxl);
872                         stripable[n]->mute_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::mute_changed, this, n), lcxl);
873                         if (stripable[n]->rec_enable_control()) {
874                                 stripable[n]->rec_enable_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::rec_changed, this, n), lcxl);
875                         }
876                 }
877                 update_track_focus_led(n);
878                 button_track_mode(track_mode());
879         }
880 }
881
882 void
883 LaunchControlXL::stripables_added ()
884 {
885         DEBUG_TRACE (DEBUG::LaunchControlXL, "LaunchControlXL::new stripable added!\n");
886         /* reload current bank */
887         switch_bank (bank_start);
888 }
889
890
891 void LaunchControlXL::set_track_mode (TrackMode mode) {
892         _track_mode = mode;
893
894         // now do led stuffs to signify the change
895         switch(mode) {
896                 case TrackMute:
897
898                         break;
899                 case TrackSolo:
900
901                         break;
902                 case TrackRecord:
903
904                         break;
905         default:
906                 break;
907         }
908 }