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