1 /*
2 * Copyright (C) 2006-2007 John Anderson
3 * Copyright (C) 2012-2017 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2012 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
6 * Copyright (C) 2015-2016 Len Ovens <len@ovenwerks.net>
7 * Copyright (C) 2015-2017 Ben Loftis <ben@harrisonconsoles.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #include <sstream>
25 #include <vector>
26 #include <climits>
27
28 #include <stdint.h>
29
30 #include <sys/time.h>
31
32 #include <glibmm/convert.h>
33
34 #include "midi++/port.h"
35
36 #include "pbd/compose.h"
37 #include "pbd/convert.h"
38
39 #include "ardour/amp.h"
40 #include "ardour/bundle.h"
41 #include "ardour/debug.h"
42 #include "ardour/midi_ui.h"
43 #include "ardour/meter.h"
44 #include "ardour/monitor_control.h"
45 #include "ardour/plugin_insert.h"
46 #include "ardour/panner.h"
47 #include "ardour/panner_shell.h"
48 #include "ardour/phase_control.h"
49 #include "ardour/rc_configuration.h"
50 #include "ardour/record_enable_control.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/send.h"
54 #include "ardour/solo_isolate_control.h"
55 #include "ardour/track.h"
56 #include "ardour/midi_track.h"
57 #include "ardour/user_bundle.h"
58 #include "ardour/profile.h"
59 #include "ardour/value_as_string.h"
60
61 #include "mackie_control_protocol.h"
62 #include "subview.h"
63 #include "surface_port.h"
64 #include "surface.h"
65 #include "strip.h"
66 #include "button.h"
67 #include "led.h"
68 #include "pot.h"
69 #include "fader.h"
70 #include "jog.h"
71 #include "meter.h"
72
73 using namespace std;
74 using namespace ARDOUR;
75 using namespace PBD;
76 using namespace ArdourSurface;
77 using namespace Mackie;
78
79 #ifndef timeradd /// only avail with __USE_BSD
80 #define timeradd(a,b,result) \
81 do { \
82 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
83 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
84 if ((result)->tv_usec >= 1000000) \
85 { \
86 ++(result)->tv_sec; \
87 (result)->tv_usec -= 1000000; \
88 } \
89 } while (0)
90 #endif
91
92 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
93
Strip(Surface & s,const std::string & name,int index,const map<Button::ID,StripButtonInfo> & strip_buttons)94 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
95 : Group (name)
96 , _solo (0)
97 , _recenable (0)
98 , _mute (0)
99 , _select (0)
100 , _vselect (0)
101 , _fader_touch (0)
102 , _vpot (0)
103 , _fader (0)
104 , _meter (0)
105 , _index (index)
106 , _surface (&s)
107 , _controls_locked (false)
108 , _transport_is_rolling (false)
109 , _metering_active (true)
110 , _block_screen_redisplay_until (0)
111 , return_to_vpot_mode_display_at (UINT64_MAX)
112 , _pan_mode (PanAzimuthAutomation)
113 , _last_gain_position_written (-1.0)
114 , _last_pan_azi_position_written (-1.0)
115 , _last_pan_width_position_written (-1.0)
116 , _last_trim_position_written (-1.0)
117 {
118 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
119 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
120
121 if (s.mcp().device_info().has_meters()) {
122 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
123 }
124
125 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
126 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
127 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
128 _surface->number(), index, Button::id_to_name (bb->bid()),
129 bb->id(), b->second.base_id));
130 }
131 }
132
~Strip()133 Strip::~Strip ()
134 {
135 /* surface is responsible for deleting all controls */
136 }
137
138 void
add(Control & control)139 Strip::add (Control & control)
140 {
141 Button* button;
142
143 Group::add (control);
144
145 /* fader, vpot, meter were all set explicitly */
146
147 if ((button = dynamic_cast<Button*>(&control)) != 0) {
148 switch (button->bid()) {
149 case Button::RecEnable:
150 _recenable = button;
151 break;
152 case Button::Mute:
153 _mute = button;
154 break;
155 case Button::Solo:
156 _solo = button;
157 break;
158 case Button::Select:
159 _select = button;
160 break;
161 case Button::VSelect:
162 _vselect = button;
163 break;
164 case Button::FaderTouch:
165 _fader_touch = button;
166 break;
167 default:
168 break;
169 }
170 }
171 }
172
173 void
set_stripable(boost::shared_ptr<Stripable> r,bool)174 Strip::set_stripable (boost::shared_ptr<Stripable> r, bool /*with_messages*/)
175 {
176 if (_controls_locked) {
177 return;
178 }
179
180 mb_pan_controllable.reset();
181
182 stripable_connections.drop_connections ();
183
184 _solo->set_control (boost::shared_ptr<AutomationControl>());
185 _mute->set_control (boost::shared_ptr<AutomationControl>());
186 _select->set_control (boost::shared_ptr<AutomationControl>());
187 _recenable->set_control (boost::shared_ptr<AutomationControl>());
188 _fader->set_control (boost::shared_ptr<AutomationControl>());
189 _vpot->set_control (boost::shared_ptr<AutomationControl>());
190
191 _stripable = r;
192
193 reset_saved_values ();
194
195 if (!r) {
196 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
197 zero ();
198 return;
199 }
200
201 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping stripable %3\n",
202 _surface->number(), _index, _stripable->name()));
203
204 _solo->set_control (_stripable->solo_control());
205 _mute->set_control (_stripable->mute_control());
206
207 _stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
208 _stripable->mute_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
209
210 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control();
211 if (pan_control) {
212 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
213 }
214
215 pan_control = _stripable->pan_width_control();
216 if (pan_control) {
217 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
218 }
219
220 _stripable->gain_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
221 _stripable->PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
222 _stripable->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
223
224 boost::shared_ptr<AutomationControl> rec_enable_control = _stripable->rec_enable_control ();
225
226 if (rec_enable_control) {
227 _recenable->set_control (rec_enable_control);
228 rec_enable_control->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
229 }
230
231 // TODO this works when a currently-banked stripable is made inactive, but not
232 // when a stripable is activated which should be currently banked.
233
234 _stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_stripable_deleted, this), ui_context());
235
236 /* setup legal VPot modes for this stripable */
237
238 possible_pot_parameters.clear();
239
240 if (_stripable->pan_azimuth_control()) {
241 possible_pot_parameters.push_back (PanAzimuthAutomation);
242 }
243 if (_stripable->pan_width_control()) {
244 possible_pot_parameters.push_back (PanWidthAutomation);
245 }
246 if (_stripable->pan_elevation_control()) {
247 possible_pot_parameters.push_back (PanElevationAutomation);
248 }
249 if (_stripable->pan_frontback_control()) {
250 possible_pot_parameters.push_back (PanFrontBackAutomation);
251 }
252 if (_stripable->pan_lfe_control()) {
253 possible_pot_parameters.push_back (PanLFEAutomation);
254 }
255
256 _pan_mode = PanAzimuthAutomation;
257
258 if (_surface->mcp().subview()->subview_mode() == Subview::None) {
259 set_vpot_parameter (_pan_mode);
260 }
261
262 _fader->set_control (_stripable->gain_control());
263
264 notify_all ();
265 }
266
267 void
notify_all()268 Strip::notify_all()
269 {
270 if (!_stripable) {
271 zero ();
272 return;
273 }
274 // The active V-pot control may not be active for this strip
275 // But if we zero it in the controls function it may erase
276 // the one we do want
277 _surface->write (_vpot->zero());
278
279 notify_solo_changed ();
280 notify_mute_changed ();
281 notify_gain_changed ();
282 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
283 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::selected));
284 notify_panner_azi_changed ();
285 notify_panner_width_changed ();
286 notify_record_enable_changed ();
287 notify_processor_changed ();
288 }
289
290 void
notify_solo_changed()291 Strip::notify_solo_changed ()
292 {
293 if (_stripable && _solo) {
294 _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
295 }
296 }
297
298 void
notify_mute_changed()299 Strip::notify_mute_changed ()
300 {
301 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
302 if (_stripable && _mute) {
303 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
304 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
305
306 _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
307 }
308 }
309
310 void
notify_record_enable_changed()311 Strip::notify_record_enable_changed ()
312 {
313 if (_stripable && _recenable) {
314 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_stripable);
315 if (trk) {
316 _surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off));
317 }
318 }
319 }
320
321 void
notify_stripable_deleted()322 Strip::notify_stripable_deleted ()
323 {
324 _surface->mcp().notify_stripable_removed ();
325 _surface->mcp().refresh_current_bank();
326 }
327
328 void
notify_gain_changed(bool force_update)329 Strip::notify_gain_changed (bool force_update)
330 {
331 if (!_stripable) {
332 return;
333 }
334
335 boost::shared_ptr<AutomationControl> ac = _stripable->gain_control();
336 Control* control;
337
338 if (!ac) {
339 /* doesn't seem possible but lets be safe */
340 return;
341 }
342
343 /* track gain control could be on vpot or fader, depending in
344 * flip mode.
345 */
346
347 if (_vpot->control() == ac) {
348 control = _vpot;
349 } else if (_fader->control() == ac) {
350 control = _fader;
351 } else {
352 return;
353 }
354
355 float gain_coefficient = ac->get_value();
356 float normalized_position = ac->internal_to_interface (gain_coefficient);
357
358 if (force_update || normalized_position != _last_gain_position_written) {
359
360 if (!control->in_use()) {
361 if (control == _vpot) {
362 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
363 } else {
364 _surface->write (_fader->set_position (normalized_position));
365 }
366 }
367
368 do_parameter_display (ac->desc(), gain_coefficient); // GainAutomation
369 _last_gain_position_written = normalized_position;
370 }
371 }
372
373 void
notify_processor_changed(bool force_update)374 Strip::notify_processor_changed (bool force_update)
375 {
376 }
377
378 void
notify_property_changed(const PropertyChange & what_changed)379 Strip::notify_property_changed (const PropertyChange& what_changed)
380 {
381 if (what_changed.contains (ARDOUR::Properties::name)) {
382 show_stripable_name ();
383 }
384
385 if (what_changed.contains (ARDOUR::Properties::selected)) {
386 _surface->write (_select->set_state (_stripable->is_selected()));
387 }
388 }
389
390 void
update_selection_state()391 Strip::update_selection_state ()
392 {
393 if(_stripable) {
394 _surface->write (_select->set_state (_stripable->is_selected()));
395 }
396 }
397
398 void
show_stripable_name()399 Strip::show_stripable_name ()
400 {
401 Subview::Mode svm = _surface->mcp().subview()->subview_mode();
402
403 if (svm != Subview::None) {
404 /* subview mode is responsible for upper line */
405 return;
406 }
407
408 string fullname = string();
409 if (!_stripable) {
410 fullname = string();
411 } else {
412 fullname = _stripable->name();
413 }
414
415 if (fullname.length() <= 6) {
416 pending_display[0] = fullname;
417 } else {
418 pending_display[0] = PBD::short_version (fullname, 6);
419 }
420 }
421
422 void
notify_panner_azi_changed(bool force_update)423 Strip::notify_panner_azi_changed (bool force_update)
424 {
425 if (!_stripable) {
426 return;
427 }
428
429 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
430
431 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
432
433 if (!pan_control) {
434 /* basically impossible, since we're here because that control
435 * changed, but sure, whatever.
436 */
437 return;
438 }
439
440 if (_vpot->control() != pan_control) {
441 return;
442 }
443
444 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value(), true);
445 double internal_pos = pan_control->get_value();
446
447 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
448
449 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
450 /* show actual internal value to user */
451 do_parameter_display (pan_control->desc(), internal_pos); // PanAzimuthAutomation
452
453 _last_pan_azi_position_written = normalized_pos;
454 }
455 }
456
457 void
notify_panner_width_changed(bool force_update)458 Strip::notify_panner_width_changed (bool force_update)
459 {
460 if (!_stripable) {
461 return;
462 }
463
464 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
465
466 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_width_control ();
467
468 if (!pan_control) {
469 /* basically impossible, since we're here because that control
470 * changed, but sure, whatever.
471 */
472 return;
473 }
474
475 if (_vpot->control() != pan_control) {
476 return;
477 }
478
479 double pos = pan_control->internal_to_interface (pan_control->get_value());
480
481 if (force_update || pos != _last_pan_width_position_written) {
482
483 _surface->write (_vpot->set (pos, true, Pot::spread));
484 do_parameter_display (pan_control->desc(), pos); // PanWidthAutomation
485
486 _last_pan_width_position_written = pos;
487 }
488 }
489
490 void
select_event(Button &,ButtonState bs)491 Strip::select_event (Button&, ButtonState bs)
492 {
493 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
494
495 if (bs == press) {
496
497 int ms = _surface->mcp().main_modifier_state();
498
499 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
500 _controls_locked = !_controls_locked;
501 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
502 block_vpot_mode_display_for (1000);
503 return;
504 }
505
506 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
507 _surface->mcp().add_down_select_button (_surface->number(), _index);
508 _surface->mcp().select_range (_surface->mcp().global_index (*this));
509
510 } else {
511 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
512 _surface->mcp().remove_down_select_button (_surface->number(), _index);
513 }
514 }
515
516 void
vselect_event(Button &,ButtonState bs)517 Strip::vselect_event (Button&, ButtonState bs)
518 {
519 if (_surface->mcp().subview()->subview_mode() != Subview::None) {
520 /* most subview modes: vpot press acts like a button for toggle parameters */
521 if (bs != press) {
522 return;
523 }
524
525 _surface->mcp().subview()->handle_vselect_event(_surface->mcp().global_index (*this));
526 return;
527 }
528
529 if (bs == press) {
530
531 int ms = _surface->mcp().main_modifier_state();
532
533 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
534
535 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
536
537 if (ac) {
538
539 /* reset to default/normal value */
540 ac->set_value (ac->normal(), Controllable::NoGroup);
541 }
542
543 } else {
544
545 #ifdef MIXBUS
546 if (_stripable) {
547 boost::shared_ptr<AutomationControl> ac = _stripable->master_send_enable_controllable ();
548 if (ac) {
549 Controllable::GroupControlDisposition gcd;
550
551 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
552 gcd = Controllable::InverseGroup;
553 } else {
554 gcd = Controllable::UseGroup;
555 }
556
557 bool enabled = ac->get_value();
558 ac->set_value (!enabled, gcd);
559 }
560 }
561 #else
562 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
563 /* switch vpot to control next available parameter */
564 next_pot_mode ();
565 #endif
566 }
567
568 }
569 }
570
571 void
fader_touch_event(Button &,ButtonState bs)572 Strip::fader_touch_event (Button&, ButtonState bs)
573 {
574 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
575
576 if (bs == press) {
577
578 boost::shared_ptr<AutomationControl> ac = _fader->control ();
579
580 _fader->set_in_use (true);
581 _fader->start_touch (_surface->mcp().transport_sample());
582
583 if (ac) {
584 do_parameter_display (ac->desc(), ac->get_value());
585 }
586
587 } else {
588
589 _fader->set_in_use (false);
590 _fader->stop_touch (_surface->mcp().transport_sample());
591
592 }
593 }
594
595
596 void
handle_button(Button & button,ButtonState bs)597 Strip::handle_button (Button& button, ButtonState bs)
598 {
599 boost::shared_ptr<AutomationControl> control;
600
601 if (bs == press) {
602 button.set_in_use (true);
603 } else {
604 button.set_in_use (false);
605 }
606
607 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
608
609 switch (button.bid()) {
610 case Button::Select:
611 select_event (button, bs);
612 break;
613
614 case Button::VSelect:
615 vselect_event (button, bs);
616 break;
617
618 case Button::FaderTouch:
619 fader_touch_event (button, bs);
620 break;
621
622 default:
623 if ((control = button.control ())) {
624 if (bs == press) {
625 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
626 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
627
628 float new_value = control->get_value() ? 0.0 : 1.0;
629
630 /* get all controls that either have their
631 * button down or are within a range of
632 * several down buttons
633 */
634
635 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
636 _surface->mcp().global_index(*this));
637
638
639 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
640 controls.size(), control->parameter().type(), new_value));
641
642 /* apply change, with potential modifier semantics */
643
644 Controllable::GroupControlDisposition gcd;
645
646 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
647 gcd = Controllable::InverseGroup;
648 } else {
649 gcd = Controllable::UseGroup;
650 }
651
652 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
653 (*c)->set_value (new_value, gcd);
654 }
655
656 } else {
657 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
658 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
659 }
660 }
661 break;
662 }
663 }
664
665 std::string
format_paramater_for_display(ARDOUR::ParameterDescriptor const & desc,float val,boost::shared_ptr<ARDOUR::Stripable> stripable_for_non_mixbus_azimuth_automation,bool & overwrite_screen_hold)666 Strip::format_paramater_for_display(
667 ARDOUR::ParameterDescriptor const& desc,
668 float val,
669 boost::shared_ptr<ARDOUR::Stripable> stripable_for_non_mixbus_azimuth_automation,
670 bool& overwrite_screen_hold)
671 {
672 std::string formatted_parameter_display;
673 char buf[16];
674
675 switch (desc.type) {
676 case GainAutomation:
677 case BusSendLevel:
678 case TrimAutomation:
679 // we can't use value_as_string() that'll suffix "dB" and also use "-inf" w/o space :(
680 if (val == 0.0) {
681 formatted_parameter_display = " -inf ";
682 } else {
683 float dB = accurate_coefficient_to_dB (val);
684 snprintf (buf, sizeof (buf), "%6.1f", dB);
685 formatted_parameter_display = buf;
686 overwrite_screen_hold = true;
687 }
688 break;
689
690 case PanAzimuthAutomation:
691 if (Profile->get_mixbus()) {
692 // XXX no _stripable check?
693 snprintf (buf, sizeof (buf), "%2.1f", val);
694 formatted_parameter_display = buf;
695 overwrite_screen_hold = true;
696 } else {
697 if (stripable_for_non_mixbus_azimuth_automation) {
698 boost::shared_ptr<AutomationControl> pa = stripable_for_non_mixbus_azimuth_automation->pan_azimuth_control();
699 if (pa) {
700 formatted_parameter_display = pa->get_user_string ();
701 overwrite_screen_hold = true;
702 }
703 }
704 }
705 break;
706 default:
707 formatted_parameter_display = ARDOUR::value_as_string (desc, val);
708 if (formatted_parameter_display.size () < 6) { // left-padding, right-align
709 formatted_parameter_display.insert (0, 6 - formatted_parameter_display.size (), ' ');
710 }
711 break;
712 }
713
714 return formatted_parameter_display;
715 }
716
717 void
do_parameter_display(ARDOUR::ParameterDescriptor const & desc,float val,bool screen_hold)718 Strip::do_parameter_display (ARDOUR::ParameterDescriptor const& desc, float val, bool screen_hold)
719 {
720 pending_display[1] = format_paramater_for_display(desc, val, _stripable, screen_hold);
721
722 if (screen_hold) {
723 /* we just queued up a parameter to be displayed.
724 1 second from now, switch back to vpot mode display.
725 */
726 block_vpot_mode_display_for (1000);
727 }
728 }
729
730 void
handle_fader_touch(Fader & fader,bool touch_on)731 Strip::handle_fader_touch (Fader& fader, bool touch_on)
732 {
733 if (touch_on) {
734 fader.start_touch (_surface->mcp().transport_sample());
735 } else {
736 fader.stop_touch (_surface->mcp().transport_sample());
737 }
738 }
739
740 void
handle_fader(Fader & fader,float position)741 Strip::handle_fader (Fader& fader, float position)
742 {
743 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
744 boost::shared_ptr<AutomationControl> ac = fader.control();
745 if (!ac) {
746 return;
747 }
748
749 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
750
751 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
752 gcd = Controllable::InverseGroup;
753 }
754
755 fader.set_value (position, gcd);
756
757 /* From the Mackie Control MIDI implementation docs:
758
759 In order to ensure absolute synchronization with the host software,
760 Mackie Control uses a closed-loop servo system for the faders,
761 meaning the faders will always move to their last received position.
762 When a host receives a Fader Position Message, it must then
763 re-transmit that message to the Mackie Control or else the faders
764 will return to their last position.
765 */
766
767 _surface->write (fader.set_position (position));
768 }
769
770 void
handle_pot(Pot & pot,float delta)771 Strip::handle_pot (Pot& pot, float delta)
772 {
773 /* Pots only emit events when they move, not when they
774 stop moving. So to get a stop event, we need to use a timeout.
775 */
776
777 boost::shared_ptr<AutomationControl> ac = pot.control();
778 if (!ac) {
779 return;
780 }
781
782 Controllable::GroupControlDisposition gcd;
783
784 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
785 gcd = Controllable::InverseGroup;
786 } else {
787 gcd = Controllable::UseGroup;
788 }
789
790 if (ac->toggled()) {
791
792 /* make it like a single-step, directional switch */
793
794 if (delta > 0) {
795 ac->set_value (1.0, gcd);
796 } else {
797 ac->set_value (0.0, gcd);
798 }
799
800 } else if (ac->desc().enumeration || ac->desc().integer_step) {
801
802 /* use Controllable::get_value() to avoid the
803 * "scaling-to-interface" that takes place in
804 * Control::get_value() via the pot member.
805 *
806 * an enumeration with 4 values will have interface values of
807 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
808 * deal with that.
809 */
810
811 if (delta > 0) {
812 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
813 } else {
814 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
815 }
816
817 } else {
818
819 double p = ac->get_interface(true);
820
821 p += delta;
822
823 p = max (0.0, p);
824 p = min (1.0, p);
825
826 ac->set_interface ( p, true);
827 }
828 }
829
830 void
periodic(PBD::microseconds_t now)831 Strip::periodic (PBD::microseconds_t now)
832 {
833 update_meter ();
834 update_automation ();
835 }
836
837 void
redisplay(PBD::microseconds_t now,bool force)838 Strip::redisplay (PBD::microseconds_t now, bool force)
839 {
840 if (_block_screen_redisplay_until >= now) {
841 /* no drawing allowed */
842 return;
843 }
844
845 if (_block_screen_redisplay_until) {
846 /* we were blocked, but the time period has elapsed, so we must
847 * force a redraw.
848 */
849 force = true;
850 _block_screen_redisplay_until = 0;
851 }
852
853 if (force || (current_display[0] != pending_display[0])) {
854 _surface->write (display (0, pending_display[0]));
855 current_display[0] = pending_display[0];
856 }
857
858 if (return_to_vpot_mode_display_at <= now) {
859 return_to_vpot_mode_display_at = UINT64_MAX;
860 return_to_vpot_mode_display ();
861 }
862
863 if (force || (current_display[1] != pending_display[1])) {
864 _surface->write (display (1, pending_display[1]));
865 current_display[1] = pending_display[1];
866 }
867 }
868
869 void
update_automation()870 Strip::update_automation ()
871 {
872 if (!_stripable) {
873 return;
874 }
875
876 ARDOUR::AutoState state = _stripable->gain_control()->automation_state();
877
878 if (state == Touch || state == Play) {
879 notify_gain_changed (false);
880 }
881
882 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control ();
883 if (pan_control) {
884 state = pan_control->automation_state ();
885 if (state == Touch || state == Play) {
886 notify_panner_azi_changed (false);
887 }
888 }
889
890 pan_control = _stripable->pan_width_control ();
891 if (pan_control) {
892 state = pan_control->automation_state ();
893 if (state == Touch || state == Play) {
894 notify_panner_width_changed (false);
895 }
896 }
897 }
898
899 void
update_meter()900 Strip::update_meter ()
901 {
902 if (!_stripable) {
903 return;
904 }
905
906 if (_surface->mcp().subview()->subview_mode() != Subview::None) {
907 return;
908 }
909
910 if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
911 float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
912 _meter->send_update (*_surface, dB);
913 return;
914 }
915 }
916
917 void
zero()918 Strip::zero ()
919 {
920 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
921 _surface->write ((*it)->zero ());
922 }
923
924 _surface->write (blank_display (0));
925 _surface->write (blank_display (1));
926 pending_display[0] = string();
927 pending_display[1] = string();
928 current_display[0] = string();
929 current_display[1] = string();
930 }
931
932 MidiByteArray
blank_display(uint32_t line_number)933 Strip::blank_display (uint32_t line_number)
934 {
935 return display (line_number, string());
936 }
937
938 MidiByteArray
display(uint32_t line_number,const std::string & line)939 Strip::display (uint32_t line_number, const std::string& line)
940 {
941 assert (line_number <= 1);
942
943 MidiByteArray retval;
944
945 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
946
947 // sysex header
948 retval << _surface->sysex_hdr();
949
950 // code for display
951 retval << 0x12;
952 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
953 retval << (_index * 7 + (line_number * 0x38));
954
955 // ascii data to display. @param line is UTF-8
956 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
957 string::size_type len = ascii.length();
958 if (len > 6) {
959 ascii = ascii.substr (0, 6);
960 len = 6;
961 }
962 retval << ascii;
963 // pad with " " out to 6 chars
964 for (int i = len; i < 6; ++i) {
965 retval << ' ';
966 }
967
968 // column spacer, unless it's the right-hand column
969 if (_index < 7) {
970 retval << ' ';
971 }
972
973 // sysex trailer
974 retval << MIDI::eox;
975
976 return retval;
977 }
978
979 void
lock_controls()980 Strip::lock_controls ()
981 {
982 _controls_locked = true;
983 }
984
985 void
unlock_controls()986 Strip::unlock_controls ()
987 {
988 _controls_locked = false;
989 }
990
991 string
vpot_mode_string()992 Strip::vpot_mode_string ()
993 {
994 if (_surface->mcp().subview()->subview_mode() != Subview::None) {
995 return string();
996 }
997
998 boost::shared_ptr<AutomationControl> ac = _vpot->control();
999
1000 if (!ac) {
1001 return string();
1002 }
1003
1004 switch (ac->desc().type) {
1005 case PanAzimuthAutomation:
1006 return "Pan";
1007 case PanWidthAutomation:
1008 return "Width";
1009 case PanElevationAutomation:
1010 return "Elev";
1011 case PanFrontBackAutomation:
1012 return "F/Rear";
1013 case PanLFEAutomation:
1014 return "LFE";
1015 default:
1016 break;
1017 }
1018 #ifdef MIXBUS
1019 //"None" mode, by definition (currently) shows the pan control above the fader.
1020 //Mixbus controllers are created from a LADSPA so they don't have ac->desc().type
1021 //For the forseeable future, we will just return "Pan" here.
1022 return "Pan";
1023 #endif
1024
1025 return "???";
1026 }
1027
1028 void
flip_mode_changed()1029 Strip::flip_mode_changed ()
1030 {
1031 if (_surface->mcp().subview()->permit_flipping_faders_and_pots()) {
1032
1033 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1034 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1035
1036 if (pot_control && fader_control) {
1037
1038 _vpot->set_control (fader_control);
1039 _fader->set_control (pot_control);
1040
1041 /* update fader with pot value */
1042
1043 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1044
1045 /* update pot with fader value */
1046
1047 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1048
1049
1050 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1051 do_parameter_display (fader_control->desc(), fader_control->get_value());
1052 } else {
1053 do_parameter_display (pot_control->desc(), pot_control->get_value()); // BusSendLevel
1054 }
1055
1056 }
1057
1058 } else {
1059 /* do nothing */
1060 }
1061 }
1062
1063 void
block_screen_display_for(uint32_t msecs)1064 Strip::block_screen_display_for (uint32_t msecs)
1065 {
1066 _block_screen_redisplay_until = PBD::get_microseconds() + (msecs * 1000);
1067 }
1068
1069 void
block_vpot_mode_display_for(uint32_t msecs)1070 Strip::block_vpot_mode_display_for (uint32_t msecs)
1071 {
1072 return_to_vpot_mode_display_at = PBD::get_microseconds() + (msecs * 1000);
1073 }
1074
1075 void
return_to_vpot_mode_display()1076 Strip::return_to_vpot_mode_display ()
1077 {
1078 /* returns the second line of the two-line per-strip display
1079 back the mode where it shows what the VPot controls.
1080 */
1081
1082 if (_surface->mcp().subview()->subview_mode() != Subview::None) {
1083 /* do nothing - second line shows value of current subview parameter */
1084 return;
1085 } else if (_stripable) {
1086 pending_display[1] = vpot_mode_string();
1087 } else {
1088 pending_display[1] = string();
1089 }
1090 }
1091
1092 void
next_pot_mode()1093 Strip::next_pot_mode ()
1094 {
1095 vector<AutomationType>::iterator i;
1096
1097 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1098 /* do not change vpot mode while in flipped mode */
1099 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1100 pending_display[1] = "Flip";
1101 block_vpot_mode_display_for (1000);
1102 return;
1103 }
1104
1105
1106 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1107
1108 if (!ac) {
1109 return;
1110 }
1111
1112
1113 if (_surface->mcp().subview()->subview_mode() != Subview::None) {
1114 return;
1115 }
1116
1117 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1118 return;
1119 }
1120
1121 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1122 if ((*i) == ac->parameter().type()) {
1123 break;
1124 }
1125 }
1126
1127 /* move to the next mode in the list, or back to the start (which will
1128 also happen if the current mode is not in the current pot mode list)
1129 */
1130
1131 if (i != possible_pot_parameters.end()) {
1132 ++i;
1133 }
1134
1135 if (i == possible_pot_parameters.end()) {
1136 i = possible_pot_parameters.begin();
1137 }
1138
1139 set_vpot_parameter (*i);
1140 }
1141
1142 void
subview_mode_changed()1143 Strip::subview_mode_changed ()
1144 {
1145 switch (_surface->mcp().subview()->subview_mode()) {
1146 case Subview::None:
1147 set_vpot_parameter (_pan_mode);
1148 /* need to show strip name again */
1149 show_stripable_name ();
1150 if (!_stripable) {
1151 _surface->write (_vpot->set (0, true, Pot::wrap));
1152 _surface->write (_fader->set_position (0.0));
1153 }
1154 notify_metering_state_changed ();
1155 break;
1156
1157 case Subview::EQ:
1158 case Subview::Dynamics:
1159 case Subview::Sends:
1160 case Subview::TrackView:
1161 case Subview::Plugin:
1162 _surface->mcp().subview()->setup_vpot(this, _vpot, pending_display);
1163 break;
1164 }
1165 }
1166
1167 void
set_vpot_parameter(AutomationType p)1168 Strip::set_vpot_parameter (AutomationType p)
1169 {
1170 if (!_stripable || (p == NullAutomation)) {
1171 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1172 pending_display[1] = string();
1173 return;
1174 }
1175
1176 boost::shared_ptr<AutomationControl> pan_control;
1177
1178 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1179
1180 reset_saved_values ();
1181
1182 switch (p) {
1183 case PanAzimuthAutomation:
1184 pan_control = _stripable->pan_azimuth_control ();
1185 break;
1186 case PanWidthAutomation:
1187 pan_control = _stripable->pan_width_control ();
1188 break;
1189 case PanElevationAutomation:
1190 break;
1191 case PanFrontBackAutomation:
1192 break;
1193 case PanLFEAutomation:
1194 break;
1195 default:
1196 return;
1197 }
1198
1199 if (pan_control) {
1200 _pan_mode = p;
1201 _vpot->set_control (pan_control);
1202 }
1203
1204 pending_display[1] = vpot_mode_string ();
1205 }
1206
1207 bool
is_midi_track() const1208 Strip::is_midi_track () const
1209 {
1210 return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
1211 }
1212
1213 void
reset_saved_values()1214 Strip::reset_saved_values ()
1215 {
1216 _last_pan_azi_position_written = -1.0;
1217 _last_pan_width_position_written = -1.0;
1218 _last_gain_position_written = -1.0;
1219 _last_trim_position_written = -1.0;
1220
1221 }
1222
1223 void
notify_metering_state_changed()1224 Strip::notify_metering_state_changed()
1225 {
1226 if (_surface->mcp().subview()->subview_mode() != Subview::None) {
1227 return;
1228 }
1229
1230 if (!_stripable || !_meter) {
1231 return;
1232 }
1233
1234 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1235 bool metering_active = _surface->mcp().metering_active ();
1236
1237 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1238 return;
1239 }
1240
1241 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1242
1243 if (!transport_is_rolling || !metering_active) {
1244 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1245 notify_panner_azi_changed (true);
1246 }
1247
1248 _transport_is_rolling = transport_is_rolling;
1249 _metering_active = metering_active;
1250 }
1251