1 /*
2  * Copyright (C) 2018-2019 Jan Lentfer <jan.lentfer@web.de>
3  * Copyright (C) 2018 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2018 Robin Gareus <robin@gareus.org>
5  * Copyright (C) 2018 Térence Clastres <t.clastres@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <stdlib.h>
23 #include <pthread.h>
24 
25 #include "pbd/compose.h"
26 #include "pbd/convert.h"
27 #include "pbd/debug.h"
28 #include "pbd/failed_constructor.h"
29 #include "pbd/file_utils.h"
30 #include "pbd/search_path.h"
31 #include "pbd/enumwriter.h"
32 
33 #include "midi++/parser.h"
34 
35 #include "ardour/amp.h"
36 #include "ardour/async_midi_port.h"
37 #include "ardour/audioengine.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/debug.h"
40 #include "ardour/midiport_manager.h"
41 #include "ardour/midi_track.h"
42 #include "ardour/midi_port.h"
43 #include "ardour/route.h"
44 #include "ardour/session.h"
45 #include "ardour/solo_isolate_control.h"
46 #include "ardour/tempo.h"
47 #include "ardour/types_convert.h"
48 #include "ardour/vca.h"
49 #include "ardour/vca_manager.h"
50 
51 
52 
53 #include "gtkmm2ext/gui_thread.h"
54 
55 #include "gui.h"
56 #include "launch_control_xl.h"
57 
58 #include "pbd/i18n.h"
59 
60 #ifdef PLATFORM_WINDOWS
61 #define random() rand()
62 #endif
63 
64 using namespace ARDOUR;
65 using namespace std;
66 using namespace PBD;
67 using namespace Glib;
68 using namespace ArdourSurface;
69 #include "pbd/abstract_ui.cc" // instantiate template
70 
71 /* init global object */
72 LaunchControlXL* lcxl = 0;
73 
LaunchControlXL(ARDOUR::Session & s)74 LaunchControlXL::LaunchControlXL (ARDOUR::Session& s)
75 	: ControlProtocol (s, string (X_("Novation Launch Control XL")))
76 	, AbstractUI<LaunchControlRequest> (name())
77 	, in_use (false)
78 	, _track_mode(TrackMute)
79 	, _template_number(8) // default template (factory 1)
80 	, _fader8master (false)
81 	, _device_mode (false)
82 #ifdef MIXBUS32C
83 	, _ctrllowersends (false)
84 	, _fss_is_mixbus (false)
85 #endif
86 	, _refresh_leds_flag (false)
87 	, _send_bank_base (0)
88 	, bank_start (0)
89 	, connection_state (ConnectionState (0))
90 	, gui (0)
91 	, in_range_select (false)
92 {
93 	lcxl = this;
94 	/* we're going to need this */
95 
96 	/* master cannot be removed, so no need to connect to going-away signal */
97 	master = session->master_out ();
98 
99 	run_event_loop ();
100 
101 	/* Ports exist for the life of this instance */
102 
103 	ports_acquire ();
104 
105 	/* Catch port connections and disconnections */
106 	ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::connection_handler, this, _1, _2, _3, _4, _5), this);
107 
108 	session->RouteAdded.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::stripables_added, this), lcxl);
109 	session->vca_manager().VCAAdded.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::stripables_added, this), lcxl);
110 }
111 
~LaunchControlXL()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_connection.disconnect ();
118 	session_connections.drop_connections ();
119 	stripable_connections.drop_connections ();
120 
121 	stop_using_device ();
122 	ports_release ();
123 
124 	stop_event_loop ();
125 	tear_down_gui ();
126 }
127 
128 
129 void
run_event_loop()130 LaunchControlXL::run_event_loop ()
131 {
132 	DEBUG_TRACE (DEBUG::LaunchControlXL, "start event loop\n");
133 	BaseUI::run ();
134 }
135 
136 void
stop_event_loop()137 LaunchControlXL::stop_event_loop ()
138 {
139 	DEBUG_TRACE (DEBUG::LaunchControlXL, "stop event loop\n");
140 	BaseUI::quit ();
141 }
142 
143 int
begin_using_device()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 	build_maps();
153 
154 	reset(template_number());
155 
156 	init_buttons (true);
157 	init_knobs ();
158 	button_track_mode(track_mode());
159 	set_send_bank(0);
160 
161 	in_use = true;
162 
163 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose("fader8master inital value  '%1'\n", fader8master()));
164 	if (fader8master()) {
165 		set_fader8master (fader8master());
166 	}
167 #ifdef MIXBUS32C
168 	if (ctrllowersends()) {
169 		set_ctrllowersends (ctrllowersends());
170 	}
171 #endif
172 
173 	return 0;
174 }
175 
176 int
stop_using_device()177 LaunchControlXL::stop_using_device ()
178 {
179 	DEBUG_TRACE (DEBUG::LaunchControlXL, "stop using device\n");
180 
181 	if (!in_use) {
182 		DEBUG_TRACE (DEBUG::LaunchControlXL, "nothing to do, device not in use\n");
183 		return 0;
184 	}
185 
186 	init_buttons (false);
187 
188 	session_connections.drop_connections ();
189 
190 	in_use = false;
191 	return 0;
192 }
193 
194 int
ports_acquire()195 LaunchControlXL::ports_acquire ()
196 {
197 	DEBUG_TRACE (DEBUG::LaunchControlXL, "acquiring ports\n");
198 
199 	/* setup ports */
200 
201 	_async_in  = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Launch Control XL in"), true);
202 	_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Launch Control XL out"), true);
203 
204 	if (_async_in == 0 || _async_out == 0) {
205 		DEBUG_TRACE (DEBUG::LaunchControlXL, "cannot register ports\n");
206 		return -1;
207 	}
208 
209 	/* We do not add our ports to the input/output bundles because we don't
210 	 * want users wiring them by hand. They could use JACK tools if they
211 	 * really insist on that (and use JACK)
212 	 */
213 
214 	_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
215 	_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
216 
217 	session->BundleAddedOrRemoved ();
218 
219 	connect_to_parser ();
220 
221 	/* Connect input port to event loop */
222 
223 	AsyncMIDIPort* asp;
224 
225 	asp = static_cast<AsyncMIDIPort*> (_input_port);
226 	asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &LaunchControlXL::midi_input_handler), _input_port));
227 	asp->xthread().attach (main_loop()->get_context());
228 
229 	return 0;
230 }
231 
232 void
ports_release()233 LaunchControlXL::ports_release ()
234 {
235 	DEBUG_TRACE (DEBUG::LaunchControlXL, "releasing ports\n");
236 
237 	/* wait for button data to be flushed */
238 	AsyncMIDIPort* asp;
239 	asp = static_cast<AsyncMIDIPort*> (_output_port);
240 	asp->drain (10000, 500000);
241 
242 	{
243 		Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
244 		AudioEngine::instance()->unregister_port (_async_in);
245 		AudioEngine::instance()->unregister_port (_async_out);
246 	}
247 
248 	_async_in.reset ((ARDOUR::Port*) 0);
249 	_async_out.reset ((ARDOUR::Port*) 0);
250 	_input_port = 0;
251 	_output_port = 0;
252 }
253 
254 list<boost::shared_ptr<ARDOUR::Bundle> >
bundles()255 LaunchControlXL::bundles ()
256 {
257 	list<boost::shared_ptr<ARDOUR::Bundle> > b;
258 
259 	if (_output_bundle) {
260 		b.push_back (_output_bundle);
261 	}
262 
263 	return b;
264 }
265 
266 void
init_knobs_and_buttons()267 LaunchControlXL::init_knobs_and_buttons()
268 {
269 	init_knobs();
270 	init_buttons();
271 }
272 
273 void
init_buttons()274 LaunchControlXL::init_buttons()
275 {
276 	init_buttons(false);
277 }
278 
279 void
init_buttons(ButtonID buttons[],uint8_t i)280 LaunchControlXL::init_buttons (ButtonID buttons[], uint8_t i)
281 {
282 	DEBUG_TRACE (DEBUG::LaunchControlXL, "init_buttons buttons[]\n");
283 	for (uint8_t n = 0; n < i; ++n) {
284 		boost::shared_ptr<TrackButton> button = boost::dynamic_pointer_cast<TrackButton> (id_note_button_map[buttons[n]]);
285 		if (button) {
286 			switch ((button->check_method)()) {
287 				case (dev_nonexistant):
288 					button->set_color(Off);
289 					break	;
290 				case (dev_inactive):
291 					button->set_color(button->color_disabled());
292 					break;
293 				case (dev_active):
294 					button->set_color(button->color_enabled());
295 					break;
296 			}
297 			DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Button %1 check_method returned: %2\n", n, (int)button->check_method()));
298 			DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Write state_msg for Button:%1\n", n));
299 			write (button->state_msg());
300 		}
301 	}
302 	/* set "Track Select" LEDs always on - we cycle through stripables */
303 	boost::shared_ptr<SelectButton> sl = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectLeft]);
304 	boost::shared_ptr<SelectButton> sr = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectRight]);
305 	if (sl && sr) {
306 		write(sl->state_msg(true));
307 		write(sr->state_msg(true));
308 	}
309 
310 	boost::shared_ptr<TrackStateButton> db =  boost::dynamic_pointer_cast<TrackStateButton>(id_note_button_map[Device]);
311 	if (db) {
312 		write(db->state_msg(device_mode()));
313 	}
314 }
315 
316 void
init_buttons(bool startup)317 LaunchControlXL::init_buttons (bool startup)
318 {
319 	DEBUG_TRACE (DEBUG::LaunchControlXL, "init_buttons (bool startup)\n");
320 	if (startup && !device_mode()) {
321 		switch_bank(bank_start);
322 		return;
323 	}
324 
325 	if (device_mode()) {
326 		ButtonID buttons[] = { Focus1, Focus2, Focus3, Focus4, Focus5, Focus6, Focus7, Focus8,
327 			Control1, Control2, Control3, Control4, Control5, Control6, Control7, Control8 };
328 
329 		for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
330 			boost::shared_ptr<TrackButton> button = boost::dynamic_pointer_cast<TrackButton> (id_note_button_map[buttons[n]]);
331 			if (button) {
332 				switch ((button->check_method)()) {
333 					case (dev_nonexistant):
334 						button->set_color(Off);
335 						break;
336 					case (dev_inactive):
337 						button->set_color(button->color_disabled());
338 						break;
339 					case (dev_active):
340 						button->set_color(button->color_enabled());
341 						break;
342 				}
343 				DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Button %1 check_method returned: %2\n", n, (int)button->check_method()));
344 				DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Write state_msg for Button:%1\n", n));
345 				write (button->state_msg());
346 			}
347 		}
348 	}
349 
350 	/* set "Track Select" LEDs always on - we cycle through stripables */
351 	boost::shared_ptr<SelectButton> sl = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectLeft]);
352 	boost::shared_ptr<SelectButton> sr = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectRight]);
353 	if (sl && sr) {
354 		write(sl->state_msg(true));
355 		write(sr->state_msg(true));
356 	}
357 #ifdef MIXBUS // for now we only offer a device mode for Mixbus
358 	boost::shared_ptr<TrackStateButton> db =  boost::dynamic_pointer_cast<TrackStateButton>(id_note_button_map[Device]);
359 	if (db) {
360 		write(db->state_msg(device_mode()));
361 	}
362 #endif
363 }
364 
365 void
init_knobs(KnobID knobs[],uint8_t i)366 LaunchControlXL::init_knobs (KnobID knobs[], uint8_t i)
367 {
368 	for (uint8_t n = 0; n < i ; ++n) {
369 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("init_knobs from array - n:%1\n", n));
370 		boost::shared_ptr<Knob> knob = id_knob_map[knobs[n]];
371 		if (knob) {
372 			switch ((knob->check_method)()) {
373 				case (dev_nonexistant):
374 					knob->set_color(Off);
375 					break;
376 				case (dev_inactive):
377 					knob->set_color(knob->color_disabled());
378 					break;
379 				case (dev_active):
380 					knob->set_color(knob->color_enabled());
381 					break;
382 			}
383 			DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Write state_msg for Knob:%1\n", n));
384 			write (knob->state_msg());
385 		}
386 	}
387 }
388 
389 
390 void
init_knobs()391 LaunchControlXL::init_knobs ()
392 {
393 	if (!device_mode()) {
394 		for (int n = 0; n < 8; ++n) {
395 			update_knob_led_by_strip(n);
396 		}
397 	} else {
398 		KnobID knobs[] = {	SendA1, SendA2, SendA3, SendA4, SendA5, SendA6, SendA7, SendA8,
399 							SendB1, SendB2, SendB3, SendB4, SendB5, SendB6, SendB7, SendB8,
400 							Pan1, Pan2, Pan3, Pan4, Pan5, Pan6, Pan7, Pan8 };
401 
402 		for (size_t n = 0; n < sizeof (knobs) / sizeof (knobs[0]); ++n) {
403 			boost::shared_ptr<Knob> knob = id_knob_map[knobs[n]];
404 			if (knob) {
405 				switch ((knob->check_method)()) {
406 					case (dev_nonexistant):
407 						knob->set_color(Off);
408 						break;
409 					case (dev_inactive):
410 						knob->set_color(knob->color_disabled());
411 						break;
412 					case (dev_active):
413 						knob->set_color(knob->color_enabled());
414 						break;
415 				}
416 
417 				DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Write state_msg for Knob:%1\n", n));
418 				write (knob->state_msg());
419 			}
420 		}
421 	}
422 }
423 
424 bool
probe()425 LaunchControlXL::probe ()
426 {
427 	return true;
428 }
429 
430 void*
request_factory(uint32_t num_requests)431 LaunchControlXL::request_factory (uint32_t num_requests)
432 {
433 	/* AbstractUI<T>::request_buffer_factory() is a template method only
434 	   instantiated in this source module. To provide something visible for
435 	   use in the interface/descriptor, we have this static method that is
436 	   template-free.
437 	*/
438 	return request_buffer_factory (num_requests);
439 }
440 
441 void
do_request(LaunchControlRequest * req)442 LaunchControlXL::do_request (LaunchControlRequest * req)
443 {
444 	if (req->type == CallSlot) {
445 
446 		call_slot (MISSING_INVALIDATOR, req->the_slot);
447 
448 	} else if (req->type == Quit) {
449 
450 		stop_using_device ();
451 	}
452 }
453 
454 void
reset(uint8_t chan)455 LaunchControlXL::reset(uint8_t chan)
456 {
457 	MidiByteArray msg (3, 176 + chan, 0, 0); // turn off all leds, reset buffer settings and duty cycle
458 
459 	write(msg);
460 }
461 int
set_active(bool yn)462 LaunchControlXL::set_active (bool yn)
463 {
464 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose("LaunchControlProtocol::set_active init with yn: '%1'\n", yn));
465 
466 	if (yn == active()) {
467 		return 0;
468 	}
469 
470 	if (yn) {
471 		if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
472 			begin_using_device ();
473 		} else {
474 			/* begin_using_device () will get called once we're connected */
475 		}
476 
477 	} else {
478 		/* Control Protocol Manager never calls us with false, but
479 		 * insteads destroys us.
480 		 */
481 	}
482 
483 	ControlProtocol::set_active (yn);
484 
485 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose("LaunchControlProtocol::set_active done with yn: '%1'\n", yn));
486 
487 	return 0;
488 }
489 
490 void
write(const MidiByteArray & data)491 LaunchControlXL::write (const MidiByteArray& data)
492 {
493 	/* immediate delivery */
494 	_output_port->write (&data[0], data.size(), 0);
495 }
496 
497 /* Device to Ardour message handling */
498 
499 bool
midi_input_handler(IOCondition ioc,MIDI::Port * port)500 LaunchControlXL::midi_input_handler (IOCondition ioc, MIDI::Port* port)
501 {
502 	if (ioc & ~IO_IN) {
503 		DEBUG_TRACE (DEBUG::LaunchControlXL, "MIDI port closed\n");
504 		return false;
505 	}
506 
507 	if (ioc & IO_IN) {
508 
509 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("something happened on  %1\n", port->name()));
510 
511 		AsyncMIDIPort* asp = static_cast<AsyncMIDIPort*>(port);
512 		if (asp) {
513 			asp->clear ();
514 		}
515 
516 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("data available on %1\n", port->name()));
517 		if (in_use) {
518 			samplepos_t now = AudioEngine::instance()->sample_time();
519 			port->parse (now);
520 		}
521 	}
522 
523 	return true;
524 }
525 
526 
527 void
connect_to_parser()528 LaunchControlXL::connect_to_parser ()
529 {
530 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Connecting to signals on port %1\n", _input_port->name()));
531 
532 	MIDI::Parser* p = _input_port->parser();
533 
534 	/* Incoming sysex */
535 	p->sysex.connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_sysex, this, _1, _2, _3));
536 
537  for (MIDI::channel_t n = 0; n < 16; ++n) {
538 	/* Controller */
539 		p->channel_controller[(int)n].connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_controller_message, this, _1, _2, n));
540 		/* Button messages are NoteOn */
541 		p->channel_note_on[(int)n].connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_note_on_message, this, _1, _2, n));
542 		/* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
543 		p->channel_note_off[(int)n].connect_same_thread (*this, boost::bind (&LaunchControlXL::handle_midi_note_off_message, this, _1, _2, n));
544 	}
545 }
546 
547 void
handle_midi_sysex(MIDI::Parser &,MIDI::byte * raw_bytes,size_t sz)548 LaunchControlXL::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
549 {
550 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Sysex, %1 bytes\n", sz));
551 
552 	if (sz < 8) {
553 		return;
554 	}
555 
556 	MidiByteArray msg (sz, raw_bytes);
557 	MidiByteArray lcxl_sysex_header (6, 0xF0, 0x00, 0x20, 0x29, 0x02, 0x11);
558 
559 	if (!lcxl_sysex_header.compare_n (msg, 6)) {
560 		return;
561 	}
562 
563 
564 	switch (msg[6]) {
565 	case 0x77: /* template change */
566 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Template change: %1\n", (int)msg[7]));
567 		_template_number = msg[7];
568 		bank_start = 0;
569 		if (!device_mode ()) {
570 			switch_bank(bank_start);
571 		} else {
572 			init_device_mode();
573 		}
574 		break;
575 	}
576 }
577 
578 
579 void
handle_button_message(boost::shared_ptr<Button> button,MIDI::EventTwoBytes * ev)580 LaunchControlXL::handle_button_message(boost::shared_ptr<Button> button, MIDI::EventTwoBytes* ev)
581 {
582 	if (ev->value) {
583 		/* any press cancels any pending long press timeouts */
584 		for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
585 			boost::shared_ptr<ControllerButton> cb = id_controller_button_map[*x];
586 			boost::shared_ptr<NoteButton>	nb = id_note_button_map[*x];
587 			if (cb != 0) {
588 				cb->timeout_connection.disconnect();
589 			} else if (nb != 0) {
590 				nb->timeout_connection.disconnect();
591 			}
592 		}
593 
594 		buttons_down.insert(button->id());
595 		DEBUG_TRACE(DEBUG::LaunchControlXL, string_compose("button pressed: %1\n", LaunchControlXL::button_name_by_id(button->id())));
596 		start_press_timeout(button, button->id());
597 	} else {
598 		DEBUG_TRACE(DEBUG::LaunchControlXL, string_compose("button depressed: %1\n", LaunchControlXL::button_name_by_id(button->id())));
599 		buttons_down.erase(button->id());
600 		button->timeout_connection.disconnect();
601 		if (button ==  id_note_button_map[Device] && refresh_leds_flag()) {
602 			switch_bank (bank_start);
603 		}
604 	}
605 
606 	set<ButtonID>::iterator c = consumed.find(button->id());
607 
608 	if (c == consumed.end()) {
609 		if (ev->value == 0) {
610 			(button->release_method)();
611 		} else {
612 			(button->press_method)();
613 		}
614 	} else {
615 		DEBUG_TRACE(DEBUG::LaunchControlXL, "button was consumed, ignored\n");
616 		consumed.erase(c);
617 	}
618 }
619 
620 
621 bool
check_pick_up(boost::shared_ptr<Controller> controller,boost::shared_ptr<AutomationControl> ac,bool rotary)622 LaunchControlXL::check_pick_up(boost::shared_ptr<Controller> controller, boost::shared_ptr<AutomationControl> ac, bool rotary)
623 {
624 	/* returns false until the controller value matches with the current setting of the stripable's ac */
625 	return (abs (controller->value() / 127.0 - ac->internal_to_interface(ac->get_value(), rotary)) < 0.007875);
626 }
627 
628 void
handle_midi_controller_message(MIDI::Parser & parser,MIDI::EventTwoBytes * ev,MIDI::channel_t chan)629 LaunchControlXL::handle_midi_controller_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev, MIDI::channel_t chan)
630 {
631 	_template_number = (int)chan;
632 
633 	if (template_number() < 8) {
634 		return; // only treat factory templates
635 	}
636 	// DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
637 
638 	CCControllerButtonMap::iterator b = cc_controller_button_map.find (ev->controller_number);
639 	CCFaderMap::iterator f = cc_fader_map.find (ev->controller_number);
640 	CCKnobMap::iterator k = cc_knob_map.find (ev->controller_number);
641 
642 	if (b != cc_controller_button_map.end()) {
643 		boost::shared_ptr<Button> button = b->second;
644 		handle_button_message(button, ev);
645 	} else if (f != cc_fader_map.end()) {
646 		boost::shared_ptr<Fader> fader = f->second;
647 		fader->set_value(ev->value);
648 		(fader->action_method)();
649 	} else if (k != cc_knob_map.end()) {
650 		boost::shared_ptr<Knob> knob = k->second;
651 		knob->set_value(ev->value);
652 		(knob->action_method)();
653 	}
654 }
655 
656 void
handle_midi_note_on_message(MIDI::Parser & parser,MIDI::EventTwoBytes * ev,MIDI::channel_t chan)657 LaunchControlXL::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev, MIDI::channel_t chan)
658 {
659 	_template_number = (int)chan;
660 
661 	if (template_number() < 8) {
662 		return; // only treat factory templates
663 	}
664 
665 	 //DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
666 
667 	 NNNoteButtonMap::iterator b = nn_note_button_map.find (ev->controller_number);
668 
669 	 if (b != nn_note_button_map.end()) {
670 		 boost::shared_ptr<Button> button = b->second;
671 		handle_button_message(button, ev);
672 	}
673 }
674 
handle_midi_note_off_message(MIDI::Parser & parser,MIDI::EventTwoBytes * ev,MIDI::channel_t chan)675 void LaunchControlXL::handle_midi_note_off_message(MIDI::Parser & parser, MIDI::EventTwoBytes *ev, MIDI::channel_t chan)
676 {
677   //DEBUG_TRACE(DEBUG::LaunchControlXL, string_compose("Note Off %1 (velocity %2)\n",(int)ev->note_number, (int)ev->velocity));
678 	handle_midi_note_on_message(parser, ev, chan); /* we handle both case in handle_midi_note_on_message */
679 }
680 
681 /* Ardour session signals connection */
682 
683 void
thread_init()684 LaunchControlXL::thread_init ()
685 {
686 	pthread_set_name (event_loop_name().c_str());
687 
688 	PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
689 	ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
690 
691 	set_thread_priority ();
692 }
693 
694 void
connect_session_signals()695 LaunchControlXL::connect_session_signals()
696 {
697 	// receive transport state changed
698 	session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_transport_state_changed, this), this);
699 	session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_loop_state_changed, this), this);
700 	// receive punch-in and punch-out
701 	Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_parameter_changed, this, _1), this);
702 	session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_parameter_changed, this, _1), this);
703 
704 	// receive rude solo changed
705 	//session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_solo_active_changed, this, _1), this);
706 	// receive record state toggled
707 	//session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::notify_record_state_changed, this), this);
708 
709 }
710 
711 
712 void
notify_transport_state_changed()713 LaunchControlXL::notify_transport_state_changed ()
714 { /*
715 	Button* b = id_button_map[Play];
716 
717 	if (session->transport_rolling()) {
718 		b->set_state (LED::OneShot24th);
719 		b->set_color (LED::GreenFull);
720 	} else {
721 
722 		 disable any blink on FixedLength from pending edit range op
723 		Button* fl = id_button_map[FixedLength];
724 
725 		fl->set_color (LED::Black);
726 		fl->set_state (LED::NoTransition);
727 		write (fl->state_msg());
728 
729 		b->set_color (LED::White);
730 		b->set_state (LED::NoTransition);
731 	}
732 
733 	write (b->state_msg()); */
734 }
735 
736 void
notify_loop_state_changed()737 LaunchControlXL::notify_loop_state_changed ()
738 {
739 }
740 
741 void
notify_parameter_changed(std::string param)742 LaunchControlXL::notify_parameter_changed (std::string param)
743 { /*
744 	IDButtonMap::iterator b;
745 
746 	if (param == "clicking") {
747 		if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
748 			return;
749 		}
750 		if (Config->get_clicking()) {
751 			b->second->set_state (LED::Blinking4th);
752 			b->second->set_color (LED::White);
753 		} else {
754 			b->second->set_color (LED::White);
755 			b->second->set_state (LED::NoTransition);
756 		}
757 		write (b->second->state_msg ()) ;
758 	} */
759 }
760 
761 /* connection handling */
762 
763 XMLNode&
get_state()764 LaunchControlXL::get_state()
765 {
766 	XMLNode& node (ControlProtocol::get_state());
767 	XMLNode* child;
768 
769 	child = new XMLNode (X_("Input"));
770 	child->add_child_nocopy (_async_in->get_state());
771 	node.add_child_nocopy (*child);
772 	child = new XMLNode (X_("Output"));
773 	child->add_child_nocopy (_async_out->get_state());
774 	node.add_child_nocopy (*child);
775 
776 	child = new XMLNode (X_("Configuration"));
777 	child->set_property ("fader8master", fader8master());
778 #ifdef MIXBUS32C
779 	child->set_property ("ctrllowersends", ctrllowersends());
780 #endif
781 	node.add_child_nocopy (*child);
782 
783 	return node;
784 }
785 
786 int
set_state(const XMLNode & node,int version)787 LaunchControlXL::set_state (const XMLNode & node, int version)
788 {
789 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("LaunchControlXL::set_state: active %1\n", active()));
790 
791 	int retval = 0;
792 
793 	if (ControlProtocol::set_state (node, version)) {
794 		return -1;
795 	}
796 
797 	XMLNode* child;
798 
799 	if ((child = node.child (X_("Input"))) != 0) {
800 		XMLNode* portnode = child->child (Port::state_node_name.c_str());
801 		if (portnode) {
802 			portnode->remove_property ("name");
803 			_async_in->set_state (*portnode, version);
804 		}
805 	}
806 
807 	if ((child = node.child (X_("Output"))) != 0) {
808 		XMLNode* portnode = child->child (Port::state_node_name.c_str());
809 		if (portnode) {
810 			portnode->remove_property ("name");
811 			_async_out->set_state (*portnode, version);
812 		}
813 	}
814 
815 	if ((child = node.child (X_("Configuration"))) !=0) {
816 		/* this should propably become a for-loop at some point */
817 		child->get_property ("fader8master", _fader8master);
818 #ifdef MIXBUS32C
819 		child->get_property ("ctrllowersends", _ctrllowersends);
820 #endif
821 	}
822 
823 	return retval;
824 }
825 
826 bool
connection_handler(boost::weak_ptr<ARDOUR::Port>,std::string name1,boost::weak_ptr<ARDOUR::Port>,std::string name2,bool yn)827 LaunchControlXL::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
828 {
829 	DEBUG_TRACE (DEBUG::LaunchControlXL, "LaunchControlXL::connection_handler start\n");
830 	if (!_async_in || !_async_out) {
831 		return false;
832 	}
833 
834 	string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
835 	string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
836 
837 	if (ni == name1 || ni == name2) {
838 		if (yn) {
839 			connection_state |= InputConnected;
840 		} else {
841 			connection_state &= ~InputConnected;
842 		}
843 	} else if (no == name1 || no == name2) {
844 		if (yn) {
845 			connection_state |= OutputConnected;
846 		} else {
847 			connection_state &= ~OutputConnected;
848 		}
849 	} else {
850 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
851 		// not our ports
852 		return false;
853 	}
854 
855 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
856 	                                           name1, name2, yn));
857 
858 	if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
859 
860 		/* XXX this is a horrible hack. Without a short sleep here,
861 		   something prevents the device wakeup messages from being
862 		   sent and/or the responses from being received.
863 		*/
864 
865 		g_usleep (100000);
866                 DEBUG_TRACE (DEBUG::LaunchControlXL, "device now connected for both input and output\n");
867 
868                 begin_using_device ();
869 
870 	} else {
871 		DEBUG_TRACE (DEBUG::LaunchControlXL, "Device disconnected (input or output or both) or not yet fully connected\n");
872 		stop_using_device ();
873 	}
874 
875 	ConnectionChange (); /* emit signal for our GUI */
876 
877 	DEBUG_TRACE (DEBUG::LaunchControlXL, "LaunchControlXL::connection_handler  end\n");
878 
879 	return true; /* connection status changed */
880 }
881 
882 
883 boost::shared_ptr<Port>
output_port()884 LaunchControlXL::output_port()
885 {
886 	return _async_out;
887 }
888 
889 boost::shared_ptr<Port>
input_port()890 LaunchControlXL::input_port()
891 {
892 	return _async_in;
893 }
894 
895 /* Stripables handling */
896 
897 void
stripable_selection_changed()898 LaunchControlXL::stripable_selection_changed ()
899 {
900 	DEBUG_TRACE (DEBUG::LaunchControlXL, "Stripable Selection changed\n");
901 	if (!device_mode()) {
902 		switch_bank (bank_start);
903 	} else {
904 #ifdef MIXBUS32C
905 		if (first_selected_stripable()) {
906 			DEBUG_TRACE (DEBUG::LaunchControlXL, "32C special handling. Checking if stripable type changed\n");
907 			bool fss_unchanged;
908 			fss_unchanged = (fss_is_mixbus() == (first_selected_stripable()->mixbus() || first_selected_stripable()->is_master()));
909 			if (!fss_unchanged) {
910 				DEBUG_TRACE (DEBUG::LaunchControlXL, "32C special handling: Stripable type DID CHANGE\n");
911 				reset(template_number());
912 				build_maps();
913 			} else {
914 				DEBUG_TRACE (DEBUG::LaunchControlXL, "32C special handling: Stripable type DID NOT CHANGE\n");
915 			}
916 		} else {
917 			reset(template_number());
918 		}
919 		store_fss_type();
920 #endif
921 		init_knobs_and_buttons();
922 		init_dm_callbacks();
923 		set_send_bank(0);
924 	}
925 }
926 
927 
928 void
stripable_property_change(PropertyChange const & what_changed,uint32_t which)929 LaunchControlXL::stripable_property_change (PropertyChange const& what_changed, uint32_t which)
930 {
931 	if (!device_mode()) {
932 		if (what_changed.contains (Properties::hidden)) {
933 			switch_bank (bank_start);
934 		}
935 
936 		if (what_changed.contains (Properties::selected)) {
937 
938 			if (!stripable[which]) {
939 				return;
940 			}
941 			if (which < 8) {
942 				update_track_focus_led ((uint8_t) which);
943 				update_knob_led_by_strip((uint8_t) which);
944 			}
945 		}
946 	} else {
947 		init_knobs_and_buttons();
948 	}
949 }
950 /* strip filter definitions */
951 
flt_default(boost::shared_ptr<Stripable> s)952 static bool flt_default (boost::shared_ptr<Stripable> s) {
953 	if (s->is_master() || s->is_monitor()) {
954 		return false;
955 	}
956 	return (boost::dynamic_pointer_cast<Route>(s) != 0 ||
957 			boost::dynamic_pointer_cast<VCA>(s) != 0);
958 }
959 
flt_track(boost::shared_ptr<Stripable> s)960 static bool flt_track (boost::shared_ptr<Stripable> s) {
961 	return boost::dynamic_pointer_cast<Track>(s) != 0;
962 }
963 
flt_auxbus(boost::shared_ptr<Stripable> s)964 static bool flt_auxbus (boost::shared_ptr<Stripable> s) {
965 	if (s->is_master() || s->is_monitor()) {
966 		return false;
967 	}
968 	if (boost::dynamic_pointer_cast<Route>(s) == 0) {
969 		return false;
970 	}
971 #ifdef MIXBUS
972 	if (s->mixbus () > 0) {
973 		return false;
974 	}
975 #endif
976 	return boost::dynamic_pointer_cast<Track>(s) == 0;
977 }
978 
979 #ifdef MIXBUS
flt_mixbus(boost::shared_ptr<Stripable> s)980 static bool flt_mixbus (boost::shared_ptr<Stripable> s) {
981 	if (s->mixbus () == 0) {
982 		return false;
983 	}
984 	return boost::dynamic_pointer_cast<Track>(s) == 0;
985 }
986 #endif
987 
flt_vca(boost::shared_ptr<Stripable> s)988 static bool flt_vca (boost::shared_ptr<Stripable> s) {
989 	return boost::dynamic_pointer_cast<VCA>(s) != 0;
990 }
991 
flt_selected(boost::shared_ptr<Stripable> s)992 static bool flt_selected (boost::shared_ptr<Stripable> s) {
993 	return s->is_selected ();
994 }
995 
996 #ifdef MIXBUS
997 #else
flt_rec_armed(boost::shared_ptr<Stripable> s)998 static bool flt_rec_armed (boost::shared_ptr<Stripable> s) {
999 	boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(s);
1000 	if (!t) {
1001 		return false;
1002 	}
1003 	return t->rec_enable_control ()->get_value () > 0;
1004 }
1005 #endif
1006 
flt_mains(boost::shared_ptr<Stripable> s)1007 static bool flt_mains (boost::shared_ptr<Stripable> s) {
1008 	return (s->is_master() || s->is_monitor());
1009 }
1010 
1011 void
filter_stripables(StripableList & strips) const1012 LaunchControlXL::filter_stripables(StripableList& strips) const
1013 {
1014 	typedef bool (*FilterFunction)(boost::shared_ptr<Stripable>);
1015 	FilterFunction flt;
1016 
1017 	switch ((int)template_number()) {
1018 		default:
1019 			/* FALLTHROUGH */
1020 		case 8:
1021 			flt = &flt_default;
1022 			break;
1023 		case 9:
1024 			flt = &flt_track;
1025 			break;
1026 		case 10:
1027 			flt = &flt_auxbus;
1028 			break;
1029 #ifdef MIXBUS
1030 		case 11:
1031 			flt = &flt_mixbus;
1032 			break;
1033 		case 12:
1034 			flt = &flt_vca;
1035 			break;
1036 #else
1037 		case 11:
1038 			flt = &flt_vca;
1039 			break;
1040 		case 12:
1041 			flt = &flt_rec_armed;
1042 			break;
1043 #endif
1044 		case 13:
1045 			flt = &flt_selected;
1046 			break;
1047 		case 14:	// Factory Template 7 behaves strange, don't map it to anyhting
1048 			flt = &flt_default;
1049 			break;
1050 		case 15:
1051 			flt = &flt_mains;
1052 			break;
1053 	}
1054 
1055 	StripableList all;
1056 	session->get_stripables (all);
1057 
1058 	for (StripableList::const_iterator s = all.begin(); s != all.end(); ++s) {
1059 		if ((*s)->is_auditioner ()) { continue; }
1060 		if ((*s)->is_hidden ()) { continue; }
1061 
1062 		if ((*flt)(*s)) {
1063 			strips.push_back (*s);
1064 		}
1065 	}
1066 	strips.sort (Stripable::Sorter(true));
1067 }
1068 
1069 void
switch_template(uint8_t t)1070 LaunchControlXL::switch_template (uint8_t t)
1071 {
1072 	MidiByteArray msg (9, 0xf0, 0x00, 0x20, 0x29, 0x02, 0x11, 0x77, t, 0xf7);
1073 	write (msg);
1074 }
1075 
1076 void
switch_bank(uint32_t base)1077 LaunchControlXL::switch_bank (uint32_t base)
1078 {
1079 	if (device_mode()) { return; }
1080 
1081 	reset(template_number());
1082 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("switch_bank bank_start:%1\n", bank_start));
1083 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("switch_bank base:%1\n", base));
1084 
1085 	StripableList strips;
1086 	filter_stripables (strips);
1087 
1088 	set_send_bank(0);
1089 
1090 	boost::shared_ptr<SelectButton> sl = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectLeft]);
1091 	boost::shared_ptr<SelectButton> sr = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectRight]);
1092 
1093 	boost::shared_ptr<Stripable> s[8];
1094 	boost::shared_ptr<Stripable> next_base;
1095 	uint32_t stripable_counter = get_amount_of_tracks();
1096 	uint32_t skip = base;
1097 	uint32_t n = 0;
1098 
1099 	for (StripableList::const_iterator strip = strips.begin(); strip != strips.end(); ++strip) {
1100 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("StripableList iterator - skip: %1, n: %2\n", skip, n));
1101 		if (skip > 0) {
1102 			--skip;
1103 			continue;
1104 		}
1105 
1106 		if (n < stripable_counter) {
1107 			DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("StripableList iterator - assigning stripable for n: %1\n", n));
1108 			s[n] = *strip;
1109 		}
1110 
1111 		if (n == stripable_counter) { /* last strip +1 -> another bank exists */
1112 			DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("StripableList iterator - n: %1. Filling next_base\n", n));
1113 			next_base = *strip;
1114 			break;
1115 		}
1116 
1117 		++n;
1118 	}
1119 
1120 	if (!s[0]) {
1121 		/* not even the first stripable exists, do nothing */
1122 		DEBUG_TRACE (DEBUG::LaunchControlXL, "not even first stripable exists.. returning\n");
1123 		return;
1124 	} else {
1125 		bank_start = base;
1126 	}
1127 
1128 	if (sl && sr) {
1129 		write(sl->state_msg(base));
1130 		write(sr->state_msg(next_base != 0));
1131 	}
1132 
1133 	stripable_connections.drop_connections ();
1134 
1135 	for (uint32_t n = 0; n < stripable_counter; ++n) {
1136 		stripable[n] = s[n];
1137 	}
1138 
1139 	for (int n = 0; n < 8; ++n) {
1140 		DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Binding Callbacks for n: %1\n", n));
1141 		if (stripable[n]) {
1142 			DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("Binding Callbacks stripable[%1] exists\n", n));
1143 
1144 			stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR,
1145 					boost::bind (&LaunchControlXL::switch_bank, this, bank_start), lcxl);
1146 			stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR,
1147 					boost::bind (&LaunchControlXL::stripable_property_change, this, _1, n), lcxl);
1148 			stripable[n]->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR,
1149 					boost::bind (&LaunchControlXL::solo_changed, this, n), lcxl);
1150 			stripable[n]->mute_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR,
1151 					boost::bind (&LaunchControlXL::mute_changed, this, n), lcxl);
1152 			if (stripable[n]->solo_isolate_control()) {	/*VCAs are stripables without isolate solo */
1153 				stripable[n]->solo_isolate_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR,
1154 						boost::bind (&LaunchControlXL::solo_iso_changed, this,n ), lcxl);
1155 			}
1156 #ifdef MIXBUS
1157 			if (stripable[n]->master_send_enable_controllable()) {
1158 				stripable[n]->master_send_enable_controllable()->Changed.connect (stripable_connections, MISSING_INVALIDATOR,
1159 						boost::bind (&LaunchControlXL::master_send_changed, this,n ), lcxl);
1160 			}
1161 #endif
1162 			if (stripable[n]->rec_enable_control()) {
1163 				stripable[n]->rec_enable_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR,
1164 						boost::bind (&LaunchControlXL::rec_changed, this, n), lcxl);
1165 
1166 			}
1167 
1168 		}
1169 		update_track_focus_led(n);
1170 		update_track_control_led(n);
1171 		update_knob_led_by_strip(n);
1172 	}
1173 	button_track_mode(track_mode());
1174 }
1175 
1176 void
init_dm_callbacks()1177 LaunchControlXL::init_dm_callbacks()
1178 {
1179 	stripable_connections.drop_connections ();
1180 
1181 	if (!first_selected_stripable()) {
1182 		return;
1183 	}
1184 	if (first_selected_stripable()->mute_control()) {
1185 		first_selected_stripable()->mute_control()->Changed.connect (stripable_connections,
1186 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_buttons,this), lcxl);
1187 	}
1188 	if (first_selected_stripable()->solo_control()) {
1189 		first_selected_stripable()->solo_control()->Changed.connect (stripable_connections,
1190 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_buttons,this), lcxl);
1191 	}
1192 	if (first_selected_stripable()->rec_enable_control()) {
1193 		first_selected_stripable()->rec_enable_control()->Changed.connect (stripable_connections,
1194 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_buttons,this), lcxl);
1195 	}
1196 #ifdef MIXBUS
1197 	if (first_selected_stripable()->eq_enable_controllable()) {
1198 		first_selected_stripable()->eq_enable_controllable()->Changed.connect (stripable_connections,
1199 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_knobs_and_buttons,this), lcxl);
1200 	}
1201 	if (first_selected_stripable()->eq_shape_controllable(0)) {
1202 		first_selected_stripable()->eq_shape_controllable(0)->Changed.connect (stripable_connections,
1203 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_buttons,this), lcxl);
1204 	}
1205 	if (first_selected_stripable()->eq_shape_controllable(3)) {
1206 		first_selected_stripable()->eq_shape_controllable(3)->Changed.connect (stripable_connections,
1207 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_buttons,this), lcxl);
1208 	}
1209 
1210 	if (first_selected_stripable()->comp_enable_controllable()) {
1211 		first_selected_stripable()->comp_enable_controllable()->Changed.connect (stripable_connections,
1212 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_knobs_and_buttons,this), lcxl);
1213 	}
1214 	if (first_selected_stripable()->filter_enable_controllable(true)) { // only handle one case, as Mixbus only has one
1215 		first_selected_stripable()->filter_enable_controllable(true)->Changed.connect (stripable_connections,
1216 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_knobs_and_buttons, this), lcxl);
1217 	}
1218 	if (first_selected_stripable()->master_send_enable_controllable()) {
1219 		first_selected_stripable()->master_send_enable_controllable()->Changed.connect (stripable_connections,
1220 		MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_knobs_and_buttons, this), lcxl);
1221 	}
1222 
1223 	for (uint8_t se = 0; se < 12 ; ++se) {
1224 		if (first_selected_stripable()->send_enable_controllable(se)) {
1225 			first_selected_stripable()->send_enable_controllable(se)->Changed.connect (stripable_connections,
1226 			MISSING_INVALIDATOR, boost::bind (&LaunchControlXL::init_knobs_and_buttons, this), lcxl);
1227 		}
1228 	}
1229 #endif
1230 }
1231 
1232 
1233 #ifdef MIXBUS32C
1234 void
store_fss_type()1235 LaunchControlXL::store_fss_type()
1236 {
1237 	if (first_selected_stripable()) {
1238 		if (first_selected_stripable()->mixbus() || first_selected_stripable()->is_master()) {
1239 			DEBUG_TRACE (DEBUG::LaunchControlXL, "Storing fss is mixbus: true\n");
1240 			_fss_is_mixbus = true;
1241 		} else {
1242 			DEBUG_TRACE (DEBUG::LaunchControlXL, "Storing fss is mixbus: false\n");
1243 			_fss_is_mixbus = false;
1244 		}
1245 	} else {
1246 		_fss_is_mixbus = false;
1247 	}
1248 }
1249 #endif
1250 
1251 void
init_device_mode()1252 LaunchControlXL::init_device_mode()
1253 {
1254 	DEBUG_TRACE (DEBUG::LaunchControlXL, "Initializing device mode\n");
1255 	init_knobs();
1256 	init_buttons(false);
1257 #ifdef MIXBUS32C
1258 	set_ctrllowersends(false);
1259 	store_fss_type();
1260 #endif
1261 	init_dm_callbacks();
1262 }
1263 
1264 void
stripables_added()1265 LaunchControlXL::stripables_added ()
1266 {
1267 	DEBUG_TRACE (DEBUG::LaunchControlXL, "LaunchControlXL::new stripable added!\n");
1268 	if (!device_mode()) {
1269 		/* reload current bank */
1270 		switch_bank (bank_start);
1271 	} else {
1272 		return;
1273 	}
1274 }
1275 
1276 
set_track_mode(TrackMode mode)1277 void LaunchControlXL::set_track_mode (TrackMode mode) {
1278 	_track_mode = mode;
1279 
1280 	// now do led stuffs to signify the change
1281 
1282 	ButtonID trk_cntrl_btns[] = {	Control1, Control2, Control3, Control4,
1283 					Control5, Control6, Control7, Control8 };
1284 
1285 	LEDColor color_on, color_off;
1286 	switch(mode) {
1287 		case TrackMute:
1288 			color_on = YellowFull;
1289 			color_off = YellowLow;
1290 			break;
1291 		case TrackSolo:
1292 			color_on = GreenFull;
1293 			color_off = GreenLow;
1294 			break;
1295 		case TrackRecord:
1296 			color_on = RedFull;
1297 			color_off = RedLow;
1298 			break;
1299 	default:
1300 		break;
1301 	}
1302 
1303 	for ( size_t n = 0 ; n < sizeof (trk_cntrl_btns) / sizeof (trk_cntrl_btns[0]); ++n) {
1304 		boost::shared_ptr<TrackButton> b = boost::dynamic_pointer_cast<TrackButton> (id_note_button_map[trk_cntrl_btns[n]]);
1305 		if (b) {
1306 			b->set_color_enabled(color_on);
1307 			b->set_color_disabled(color_off);
1308 		}
1309 	}
1310 }
1311 
1312 void
set_device_mode(bool yn)1313 LaunchControlXL::set_device_mode (bool yn)
1314 {
1315 	_device_mode = yn;
1316 	reset(template_number());
1317 	boost::shared_ptr<TrackStateButton> db =  boost::dynamic_pointer_cast<TrackStateButton>(id_note_button_map[Device]);
1318 	write(db->state_msg(_device_mode));
1319 	set_send_bank(0);
1320 	build_maps();
1321 	if (device_mode()) {
1322 		init_device_mode();
1323 	} else {
1324 #ifdef MIXBUS32C
1325 		set_ctrllowersends(ctrllowersends());
1326 #endif
1327 		switch_bank (bank_start);
1328 	}
1329 }
1330 
1331 
1332 void
set_fader8master(bool yn)1333 LaunchControlXL::set_fader8master (bool yn)
1334 {
1335 	_fader8master = yn;
1336 	if (_fader8master) {
1337 		stripable[7] = master;
1338 		if (bank_start > 0) {
1339 			bank_start -= 1;
1340 		}
1341 	} else {
1342 		if (bank_start > 0) {
1343 			bank_start += 1;
1344 		}
1345 	}
1346 
1347 	switch_bank (bank_start);
1348 }
1349 
1350 #ifdef MIXBUS32C
1351 void
set_ctrllowersends(bool yn)1352 LaunchControlXL::set_ctrllowersends (bool yn)
1353 {
1354 
1355 	_ctrllowersends = yn;
1356 
1357 	if (device_mode()) { return; }
1358 
1359 	/* reinit the send bank */
1360 	if (_ctrllowersends) {
1361 		_send_bank_base = 6;
1362 	} else {
1363 		_send_bank_base = 0;
1364 	}
1365 	set_send_bank(0);
1366 }
1367 #endif
1368 
1369 void
set_send_bank(int offset)1370 LaunchControlXL::set_send_bank (int offset)
1371 {
1372 
1373 	int lowersendsoffset = 0;
1374 
1375 #ifdef MIXBUS32C
1376 	if (ctrllowersends() && !device_mode()) {
1377 		lowersendsoffset = 6;
1378 	}
1379 #endif
1380 	if ((_send_bank_base == (0 + lowersendsoffset)  && offset < 0) || (_send_bank_base == (4 + lowersendsoffset) && offset > 0)) {
1381 		return;
1382 	}
1383 
1384 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("set_send_bank - _send_bank_base: %1 \n", send_bank_base()));
1385 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("set_send_bank - applying offset %1 \n", offset));
1386 
1387 	boost::shared_ptr<SelectButton> sbu = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectUp]);
1388 	boost::shared_ptr<SelectButton> sbd = boost::dynamic_pointer_cast<SelectButton>(id_controller_button_map[SelectDown]);
1389 
1390 	if (!sbu || !sbd ) {
1391 		return;
1392 	}
1393 
1394 	_send_bank_base = _send_bank_base + offset;
1395 	_send_bank_base = max (0 + lowersendsoffset, min (4 + lowersendsoffset, _send_bank_base));
1396 
1397 	DEBUG_TRACE (DEBUG::LaunchControlXL, string_compose ("set_send_bank - _send_bank_base: %1 \n", send_bank_base()));
1398 
1399 
1400 #ifdef MIXBUS
1401 	if (device_mode()) {	/* in device mode rebuild send led bindings */
1402 		build_maps();
1403 		//init_knobs_and_buttons();
1404 		KnobID knobs[] = { Pan1, Pan2, Pan3, Pan4, Pan5, Pan6, Pan7, Pan8 };
1405 		ButtonID buttons[] = { Focus1, Focus2, Focus3, Focus4, Focus5, Focus6, Focus7, Focus8 };
1406 		init_knobs (knobs, 8);
1407 		init_buttons (buttons, 8);
1408 	}
1409 #endif
1410 	switch (_send_bank_base) {
1411 		case 0:
1412 		case 1:
1413 		case 6:
1414 		case 7:
1415 			write (sbu->state_msg(false));
1416 			write (sbd->state_msg(true));
1417 			break;
1418 		case 2:
1419 		case 3:
1420 		case 8:
1421 		case 9:
1422 			write (sbu->state_msg(true));
1423 			write (sbd->state_msg(true));
1424 			break;
1425 		case 4:
1426 		case 5:
1427 		case 10:
1428 		case 11:
1429 			write (sbu->state_msg(true));
1430 			write (sbd->state_msg(false));
1431 			break;
1432 	}
1433 }
1434 
1435 int
get_amount_of_tracks()1436 LaunchControlXL::get_amount_of_tracks ()
1437 {
1438 	int no_of_tracks;
1439 	if (fader8master ()) {
1440 		no_of_tracks = 7;
1441         } else {
1442                 no_of_tracks = 8;
1443 	}
1444 
1445 	return no_of_tracks;
1446 }
1447 
1448 void
set_refresh_leds_flag(bool yn)1449 LaunchControlXL::set_refresh_leds_flag (bool yn)
1450 {
1451 	_refresh_leds_flag = yn;
1452 }
1453