2 Copyright (C) 2006,2007 John Anderson
3 Copyright (C) 2012 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <glibmm/convert.h>
30 #include "midi++/port.h"
32 #include "pbd/compose.h"
33 #include "pbd/convert.h"
35 #include "ardour/amp.h"
36 #include "ardour/bundle.h"
37 #include "ardour/debug.h"
38 #include "ardour/midi_ui.h"
39 #include "ardour/meter.h"
40 #include "ardour/plugin_insert.h"
41 #include "ardour/pannable.h"
42 #include "ardour/panner.h"
43 #include "ardour/panner_shell.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/route.h"
46 #include "ardour/session.h"
47 #include "ardour/send.h"
48 #include "ardour/track.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/user_bundle.h"
51 #include "ardour/profile.h"
53 #include "mackie_control_protocol.h"
54 #include "surface_port.h"
65 using namespace ARDOUR;
67 using namespace ArdourSurface;
68 using namespace Mackie;
70 #ifndef timeradd /// only avail with __USE_BSD
71 #define timeradd(a,b,result) \
73 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
74 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
75 if ((result)->tv_usec >= 1000000) \
78 (result)->tv_usec -= 1000000; \
83 #define ui_context() MackieControlProtocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
85 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
98 , _controls_locked (false)
99 , _transport_is_rolling (false)
100 , _metering_active (true)
101 , _block_screen_redisplay_until (0)
102 , return_to_vpot_mode_display_at (UINT64_MAX)
104 , _pan_mode (PanAzimuthAutomation)
105 , _last_gain_position_written (-1.0)
106 , _last_pan_azi_position_written (-1.0)
107 , _last_pan_width_position_written (-1.0)
108 , _last_trim_position_written (-1.0)
110 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
111 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
113 if (s.mcp().device_info().has_meters()) {
114 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
117 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
118 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
119 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
120 _surface->number(), index, Button::id_to_name (bb->bid()),
121 bb->id(), b->second.base_id));
127 /* surface is responsible for deleting all controls */
131 Strip::add (Control & control)
135 Group::add (control);
137 /* fader, vpot, meter were all set explicitly */
139 if ((button = dynamic_cast<Button*>(&control)) != 0) {
140 switch (button->bid()) {
141 case Button::RecEnable:
153 case Button::VSelect:
156 case Button::FaderTouch:
157 _fader_touch = button;
166 Strip::set_route (boost::shared_ptr<Route> r, bool /*with_messages*/)
168 if (_controls_locked) {
172 mb_pan_controllable.reset();
174 route_connections.drop_connections ();
176 _solo->set_control (boost::shared_ptr<AutomationControl>());
177 _mute->set_control (boost::shared_ptr<AutomationControl>());
178 _select->set_control (boost::shared_ptr<AutomationControl>());
179 _recenable->set_control (boost::shared_ptr<AutomationControl>());
180 _fader->set_control (boost::shared_ptr<AutomationControl>());
181 _vpot->set_control (boost::shared_ptr<AutomationControl>());
185 reset_saved_values ();
192 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
193 _surface->number(), _index, _route->name()));
195 _solo->set_control (_route->solo_control());
196 _mute->set_control (_route->mute_control());
198 _route->solo_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
199 _route->listen_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
201 _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
203 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control();
205 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
208 pan_control = _route->pan_width_control();
210 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
213 _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
214 _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
216 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
219 _recenable->set_control (trk->rec_enable_control());
220 trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
223 // TODO this works when a currently-banked route is made inactive, but not
224 // when a route is activated which should be currently banked.
226 _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context());
227 _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context());
229 /* setup legal VPot modes for this route */
231 possible_pot_parameters.clear();
233 if (_route->pan_azimuth_control()) {
234 possible_pot_parameters.push_back (PanAzimuthAutomation);
236 if (_route->pan_width_control()) {
237 possible_pot_parameters.push_back (PanWidthAutomation);
239 if (_route->pan_elevation_control()) {
240 possible_pot_parameters.push_back (PanElevationAutomation);
242 if (_route->pan_frontback_control()) {
243 possible_pot_parameters.push_back (PanFrontBackAutomation);
245 if (_route->pan_lfe_control()) {
246 possible_pot_parameters.push_back (PanLFEAutomation);
249 _pan_mode = PanAzimuthAutomation;
251 if (_surface->mcp().subview_mode() == MackieControlProtocol::None) {
252 set_vpot_parameter (_pan_mode);
255 _fader->set_control (_route->gain_control());
267 // The active V-pot control may not be active for this strip
268 // But if we zero it in the controls function it may erase
269 // the one we do want
270 _surface->write (_vpot->zero());
272 notify_solo_changed ();
273 notify_mute_changed ();
274 notify_gain_changed ();
275 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
276 notify_panner_azi_changed ();
277 notify_panner_width_changed ();
278 notify_record_enable_changed ();
279 notify_processor_changed ();
283 Strip::notify_solo_changed ()
285 if (_route && _solo) {
286 _surface->write (_solo->set_state ((_route->soloed() || _route->listening_via_monitor()) ? on : off));
291 Strip::notify_mute_changed ()
293 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
294 if (_route && _mute) {
295 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted()));
296 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off)));
298 _surface->write (_mute->set_state (_route->muted() ? on : off));
303 Strip::notify_record_enable_changed ()
305 if (_route && _recenable) {
306 _surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
311 Strip::notify_active_changed ()
313 _surface->mcp().refresh_current_bank();
317 Strip::notify_route_deleted ()
319 _surface->mcp().refresh_current_bank();
323 Strip::notify_gain_changed (bool force_update)
329 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
333 /* doesn't seem possible but lets be safe */
337 /* track gain control could be on vpot or fader, depending in
341 if (_vpot->control() == ac) {
343 } else if (_fader->control() == ac) {
349 float gain_coefficient = ac->get_value();
350 float normalized_position = ac->internal_to_interface (gain_coefficient);
352 if (force_update || normalized_position != _last_gain_position_written) {
354 if (!control->in_use()) {
355 if (control == _vpot) {
356 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
358 _surface->write (_fader->set_position (normalized_position));
362 do_parameter_display (GainAutomation, gain_coefficient);
363 _last_gain_position_written = normalized_position;
368 Strip::notify_processor_changed (bool force_update)
373 Strip::notify_property_changed (const PropertyChange& what_changed)
375 if (!what_changed.contains (ARDOUR::Properties::name)) {
383 Strip::show_route_name ()
385 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
387 if (svm != MackieControlProtocol::None) {
388 /* subview mode is responsible for upper line */
392 string fullname = string();
396 fullname = _route->name();
399 if (fullname.length() <= 6) {
400 pending_display[0] = fullname;
402 pending_display[0] = PBD::short_version (fullname, 6);
407 Strip::notify_send_level_change (AutomationType type, uint32_t send_num, bool force_update)
409 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
412 /* not in subview mode */
416 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
417 /* no longer in Sends subview mode */
421 boost::shared_ptr<AutomationControl> control = r->send_level_controllable (send_num);
427 float val = control->get_value();
428 do_parameter_display (type, val);
430 if (_vpot->control() == control) {
431 /* update pot/encoder */
432 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
438 Strip::notify_trackview_change (AutomationType type, uint32_t send_num, bool force_update)
440 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
443 /* not in subview mode */
447 if (_surface->mcp().subview_mode() != MackieControlProtocol::TrackView) {
448 /* no longer in TrackViewsubview mode */
452 boost::shared_ptr<AutomationControl> control;
453 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
457 control = r->trim_control();
459 case SoloIsolateAutomation:
460 control = r->solo_isolate_control ();
462 case SoloSafeAutomation:
463 control = r->solo_safe_control ();
465 case MonitoringAutomation:
467 control = track->monitoring_control();
470 case PhaseAutomation:
471 control = r->phase_control ();
478 float val = control->get_value();
480 /* Note: all of the displayed controllables require the display
481 * of their *actual* ("internal") value, not the version mapped
482 * into the normalized 0..1.0 ("interface") range.
485 do_parameter_display (type, val);
486 /* update pot/encoder */
487 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
492 Strip::notify_eq_change (AutomationType type, uint32_t band, bool force_update)
494 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
497 /* not in subview mode */
501 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
502 /* no longer in EQ subview mode */
506 boost::shared_ptr<AutomationControl> control;
510 control = r->eq_gain_controllable (band);
513 control = r->eq_freq_controllable (band);
516 control = r->eq_q_controllable (band);
519 control = r->eq_shape_controllable (band);
522 control = r->eq_hpf_controllable ();
525 control = r->eq_enable_controllable ();
532 float val = control->get_value();
533 do_parameter_display (type, val);
534 /* update pot/encoder */
535 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
540 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
542 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
545 /* not in subview mode */
549 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
550 /* no longer in EQ subview mode */
554 boost::shared_ptr<AutomationControl> control;
555 bool reset_all = false;
559 control = r->comp_threshold_controllable ();
562 control = r->comp_speed_controllable ();
565 control = r->comp_mode_controllable ();
569 control = r->comp_makeup_controllable ();
572 control = r->comp_redux_controllable ();
575 control = r->comp_enable_controllable ();
581 if (propagate_mode && reset_all) {
582 _surface->subview_mode_changed ();
586 float val = control->get_value();
587 do_parameter_display (type, val);
588 /* update pot/encoder */
589 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
594 Strip::notify_panner_azi_changed (bool force_update)
600 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
602 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
605 /* basically impossible, since we're here because that control
606 * changed, but sure, whatever.
611 if (_vpot->control() != pan_control) {
615 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
616 double internal_pos = pan_control->get_value();
618 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
620 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
621 /* show actual internal value to user */
622 do_parameter_display (PanAzimuthAutomation, internal_pos);
624 _last_pan_azi_position_written = normalized_pos;
629 Strip::notify_panner_width_changed (bool force_update)
635 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
637 boost::shared_ptr<AutomationControl> pan_control = _route->pan_width_control ();
640 /* basically impossible, since we're here because that control
641 * changed, but sure, whatever.
646 if (_vpot->control() != pan_control) {
650 double pos = pan_control->internal_to_interface (pan_control->get_value());
652 if (force_update || pos != _last_pan_width_position_written) {
654 _surface->write (_vpot->set (pos, true, Pot::spread));
655 do_parameter_display (PanWidthAutomation, pos);
657 _last_pan_width_position_written = pos;
662 Strip::select_event (Button&, ButtonState bs)
664 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
668 int ms = _surface->mcp().main_modifier_state();
670 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
671 _controls_locked = !_controls_locked;
672 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
673 block_vpot_mode_display_for (1000);
677 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
678 _surface->mcp().add_down_select_button (_surface->number(), _index);
679 _surface->mcp().select_range ();
682 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
683 _surface->mcp().remove_down_select_button (_surface->number(), _index);
688 Strip::vselect_event (Button&, ButtonState bs)
690 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
692 /* most subview modes: vpot press acts like a button for toggle parameters */
698 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
700 boost::shared_ptr<AutomationControl> control = _vpot->control ();
705 Controllable::GroupControlDisposition gcd;
706 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
707 gcd = Controllable::InverseGroup;
709 gcd = Controllable::UseGroup;
712 if (control->toggled()) {
713 if (control->toggled()) {
714 control->set_value (!control->get_value(), gcd);
717 } else if (control->desc().enumeration || control->desc().integer_step) {
719 double val = control->get_value ();
720 if (val <= control->upper() - 1.0) {
721 control->set_value (val + 1.0, gcd);
723 control->set_value (control->lower(), gcd);
729 /* Send mode: press enables/disables the relevant
730 * send, but the vpot is bound to the send-level so we
731 * need to lookup the enable/disable control
735 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
739 const uint32_t global_pos = _surface->mcp().global_index (*this);
740 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
743 bool currently_enabled = (bool) control->get_value();
744 Controllable::GroupControlDisposition gcd;
746 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
747 gcd = Controllable::InverseGroup;
749 gcd = Controllable::UseGroup;
752 control->set_value (!currently_enabled, gcd);
754 if (currently_enabled) {
755 /* we just turned it off */
756 pending_display[1] = "off";
758 /* we just turned it on, show the level
760 control = _route->send_level_controllable (global_pos);
761 do_parameter_display (BusSendLevel, control->get_value());
767 /* done with this event in subview mode */
774 int ms = _surface->mcp().main_modifier_state();
776 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
778 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
782 /* reset to default/normal value */
783 ac->set_value (ac->normal(), Controllable::NoGroup);
790 boost::shared_ptr<AutomationControl> ac = _route->master_send_enable_controllable ();
792 Controllable::GroupControlDisposition gcd;
794 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
795 gcd = Controllable::InverseGroup;
797 gcd = Controllable::UseGroup;
800 bool enabled = ac->get_value();
801 ac->set_value (!enabled, gcd);
805 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
806 /* switch vpot to control next available parameter */
815 Strip::fader_touch_event (Button&, ButtonState bs)
817 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
821 boost::shared_ptr<AutomationControl> ac = _fader->control ();
823 _fader->set_in_use (true);
824 _fader->start_touch (_surface->mcp().transport_frame());
827 do_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
832 _fader->set_in_use (false);
833 _fader->stop_touch (_surface->mcp().transport_frame(), true);
840 Strip::handle_button (Button& button, ButtonState bs)
842 boost::shared_ptr<AutomationControl> control;
845 button.set_in_use (true);
847 button.set_in_use (false);
850 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
852 switch (button.bid()) {
854 select_event (button, bs);
857 case Button::VSelect:
858 vselect_event (button, bs);
861 case Button::FaderTouch:
862 fader_touch_event (button, bs);
866 if ((control = button.control ())) {
868 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
869 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
871 float new_value = control->get_value() ? 0.0 : 1.0;
873 /* get all controls that either have their
874 * button down or are within a range of
875 * several down buttons
878 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
881 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
882 controls.size(), control->parameter().type(), new_value));
884 /* apply change, with potential modifier semantics */
886 Controllable::GroupControlDisposition gcd;
888 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
889 gcd = Controllable::InverseGroup;
891 gcd = Controllable::UseGroup;
894 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
895 (*c)->set_value (new_value, gcd);
899 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
900 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
908 Strip::do_parameter_display (AutomationType type, float val)
910 bool screen_hold = false;
916 pending_display[1] = " -inf ";
918 float dB = accurate_coefficient_to_dB (val);
919 snprintf (buf, sizeof (buf), "%6.1f", dB);
920 pending_display[1] = buf;
926 if (Profile->get_mixbus()) { //Mixbus sends are already stored in dB
927 snprintf (buf, sizeof (buf), "%2.1f", val);
928 pending_display[1] = buf;
932 pending_display[1] = " -inf ";
934 float dB = accurate_coefficient_to_dB (val);
935 snprintf (buf, sizeof (buf), "%6.1f", dB);
936 pending_display[1] = buf;
942 case PanAzimuthAutomation:
943 if (Profile->get_mixbus()) {
944 snprintf (buf, sizeof (buf), "%2.1f", val);
945 pending_display[1] = buf;
949 boost::shared_ptr<Pannable> p = _route->pannable();
950 if (p && _route->panner()) {
951 pending_display[1] =_route->panner()->value_as_string (p->pan_azimuth_control);
958 case PanWidthAutomation:
960 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
961 pending_display[1] = buf;
968 float dB = accurate_coefficient_to_dB (val);
969 snprintf (buf, sizeof (buf), "%6.1f", dB);
970 pending_display[1] = buf;
975 case PhaseAutomation:
978 pending_display[1] = "Normal";
980 pending_display[1] = "Invert";
995 snprintf (buf, sizeof (buf), "%6.1f", val);
996 pending_display[1] = buf;
1002 pending_display[1] = "on";
1004 pending_display[1] = "off";
1008 if (_surface->mcp().subview_route()) {
1009 pending_display[1] = _surface->mcp().subview_route()->comp_mode_name (val);
1012 case SoloSafeAutomation:
1013 case SoloIsolateAutomation:
1015 pending_display[1] = "on";
1017 pending_display[1] = "off";
1020 case MonitoringAutomation:
1021 switch (MonitorChoice ((int) val)) {
1023 pending_display[1] = "auto";
1026 pending_display[1] = "input";
1029 pending_display[1] = "disk";
1031 case MonitorCue: /* XXX not implemented as of jan 2016 */
1032 pending_display[1] = "cue";
1041 /* we just queued up a parameter to be displayed.
1042 1 second from now, switch back to vpot mode display.
1044 block_vpot_mode_display_for (1000);
1049 Strip::handle_fader_touch (Fader& fader, bool touch_on)
1052 fader.start_touch (_surface->mcp().transport_frame());
1054 fader.stop_touch (_surface->mcp().transport_frame(), false);
1059 Strip::handle_fader (Fader& fader, float position)
1061 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1062 boost::shared_ptr<AutomationControl> ac = fader.control();
1067 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1069 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1070 gcd = Controllable::InverseGroup;
1073 fader.set_value (position, gcd);
1075 /* From the Mackie Control MIDI implementation docs:
1077 In order to ensure absolute synchronization with the host software,
1078 Mackie Control uses a closed-loop servo system for the faders,
1079 meaning the faders will always move to their last received position.
1080 When a host receives a Fader Position Message, it must then
1081 re-transmit that message to the Mackie Control or else the faders
1082 will return to their last position.
1085 _surface->write (fader.set_position (position));
1089 Strip::handle_pot (Pot& pot, float delta)
1091 /* Pots only emit events when they move, not when they
1092 stop moving. So to get a stop event, we need to use a timeout.
1095 boost::shared_ptr<AutomationControl> ac = pot.control();
1100 Controllable::GroupControlDisposition gcd;
1102 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1103 gcd = Controllable::InverseGroup;
1105 gcd = Controllable::UseGroup;
1108 if (ac->toggled()) {
1110 /* make it like a single-step, directional switch */
1113 ac->set_value (1.0, gcd);
1115 ac->set_value (0.0, gcd);
1118 } else if (ac->desc().enumeration || ac->desc().integer_step) {
1120 /* use Controllable::get_value() to avoid the
1121 * "scaling-to-interface" that takes place in
1122 * Control::get_value() via the pot member.
1124 * an enumeration with 4 values will have interface values of
1125 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
1130 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
1132 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
1137 double p = ac->get_interface();
1144 ac->set_value ( ac->interface_to_internal(p), gcd);
1149 Strip::periodic (ARDOUR::microseconds_t now)
1152 update_automation ();
1156 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1158 if (_block_screen_redisplay_until >= now) {
1159 /* no drawing allowed */
1163 if (_block_screen_redisplay_until) {
1164 /* we were blocked, but the time period has elapsed, so we must
1168 _block_screen_redisplay_until = 0;
1171 if (force || (current_display[0] != pending_display[0])) {
1172 _surface->write (display (0, pending_display[0]));
1173 current_display[0] = pending_display[0];
1176 if (return_to_vpot_mode_display_at <= now) {
1177 return_to_vpot_mode_display_at = UINT64_MAX;
1178 return_to_vpot_mode_display ();
1181 if (force || (current_display[1] != pending_display[1])) {
1182 _surface->write (display (1, pending_display[1]));
1183 current_display[1] = pending_display[1];
1188 Strip::update_automation ()
1194 ARDOUR::AutoState state = _route->gain_control()->automation_state();
1196 if (state == Touch || state == Play) {
1197 notify_gain_changed (false);
1200 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
1202 state = pan_control->automation_state ();
1203 if (state == Touch || state == Play) {
1204 notify_panner_azi_changed (false);
1208 pan_control = _route->pan_width_control ();
1210 state = pan_control->automation_state ();
1211 if (state == Touch || state == Play) {
1212 notify_panner_width_changed (false);
1218 Strip::update_meter ()
1224 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1228 if (_meter && _transport_is_rolling && _metering_active) {
1229 float dB = const_cast<PeakMeter&> (_route->peak_meter()).meter_level (0, MeterMCP);
1230 _meter->send_update (*_surface, dB);
1238 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1239 _surface->write ((*it)->zero ());
1242 _surface->write (blank_display (0));
1243 _surface->write (blank_display (1));
1244 pending_display[0] = string();
1245 pending_display[1] = string();
1246 current_display[0] = string();
1247 current_display[1] = string();
1251 Strip::blank_display (uint32_t line_number)
1253 return display (line_number, string());
1257 Strip::display (uint32_t line_number, const std::string& line)
1259 assert (line_number <= 1);
1261 MidiByteArray retval;
1263 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1266 retval << _surface->sysex_hdr();
1270 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1271 retval << (_index * 7 + (line_number * 0x38));
1273 // ascii data to display. @param line is UTF-8
1274 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1275 string::size_type len = ascii.length();
1277 ascii = ascii.substr (0, 6);
1281 // pad with " " out to 6 chars
1282 for (int i = len; i < 6; ++i) {
1286 // column spacer, unless it's the right-hand column
1292 retval << MIDI::eox;
1298 Strip::lock_controls ()
1300 _controls_locked = true;
1304 Strip::unlock_controls ()
1306 _controls_locked = false;
1310 Strip::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& rl)
1312 for (ARDOUR::StrongRouteNotificationList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
1313 if ((*i) == _route) {
1314 _surface->write (_select->set_state (on));
1319 _surface->write (_select->set_state (off));
1323 Strip::vpot_mode_string ()
1325 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1329 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1335 switch (ac->desc().type) {
1336 case PanAzimuthAutomation:
1338 case PanWidthAutomation:
1340 case PanElevationAutomation:
1342 case PanFrontBackAutomation:
1344 case PanLFEAutomation:
1354 Strip::flip_mode_changed ()
1356 if (_surface->mcp().subview_mode() == MackieControlProtocol::Sends) {
1358 boost::shared_ptr<AutomationControl> pot_control = _vpot->control();
1359 boost::shared_ptr<AutomationControl> fader_control = _fader->control();
1361 if (pot_control && fader_control) {
1362 _vpot->set_control (fader_control);
1363 _fader->set_control (pot_control);
1366 if (_surface->mcp().flip_mode() == MackieControlProtocol::Normal) {
1367 do_parameter_display (GainAutomation, fader_control->get_value());
1369 do_parameter_display (BusSendLevel, fader_control->get_value());
1374 _surface->write (_fader->set_position (pot_control->internal_to_interface (pot_control->get_value ())));
1378 _surface->write (_vpot->set (fader_control->internal_to_interface (fader_control->get_value()), true, Pot::wrap));
1387 Strip::block_screen_display_for (uint32_t msecs)
1389 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1393 Strip::block_vpot_mode_display_for (uint32_t msecs)
1395 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1399 Strip::return_to_vpot_mode_display ()
1401 /* returns the second line of the two-line per-strip display
1402 back the mode where it shows what the VPot controls.
1405 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1406 /* do nothing - second line shows value of current subview parameter */
1408 } else if (_route) {
1409 pending_display[1] = vpot_mode_string();
1411 pending_display[1] = string();
1416 Strip::next_pot_mode ()
1418 vector<AutomationType>::iterator i;
1420 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1421 /* do not change vpot mode while in flipped mode */
1422 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1423 pending_display[1] = "Flip";
1424 block_vpot_mode_display_for (1000);
1429 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1436 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1440 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1444 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1445 if ((*i) == ac->parameter().type()) {
1450 /* move to the next mode in the list, or back to the start (which will
1451 also happen if the current mode is not in the current pot mode list)
1454 if (i != possible_pot_parameters.end()) {
1458 if (i == possible_pot_parameters.end()) {
1459 i = possible_pot_parameters.begin();
1462 set_vpot_parameter (*i);
1466 Strip::subview_mode_changed ()
1468 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
1470 subview_connections.drop_connections ();
1472 switch (_surface->mcp().subview_mode()) {
1473 case MackieControlProtocol::None:
1474 set_vpot_parameter (_pan_mode);
1475 /* need to show strip name again */
1478 _surface->write (_vpot->set (0, true, Pot::wrap));
1479 _surface->write (_fader->set_position (0.0));
1481 notify_metering_state_changed ();
1485 case MackieControlProtocol::EQ:
1489 /* leave it as it was */
1493 case MackieControlProtocol::Dynamics:
1497 /* leave it as it was */
1502 case MackieControlProtocol::Sends:
1504 setup_sends_vpot (r);
1506 /* leave it as it was */
1510 case MackieControlProtocol::TrackView:
1512 setup_trackview_vpot (r);
1514 /* leave it as it was */
1522 Strip::setup_dyn_vpot (boost::shared_ptr<Route> r)
1528 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1529 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1530 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1531 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1532 boost::shared_ptr<AutomationControl> rc = r->comp_redux_controllable ();
1533 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1535 uint32_t pos = _surface->mcp().global_index (*this);
1537 /* we will control the pos-th available parameter, from the list in the
1538 * order shown above.
1541 vector<boost::shared_ptr<AutomationControl> > available;
1542 vector<AutomationType> params;
1544 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1545 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1546 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1547 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1548 if (rc) { available.push_back (rc); params.push_back (CompRedux); }
1549 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1551 if (pos >= available.size()) {
1552 /* this knob is not needed to control the available parameters */
1553 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1554 pending_display[0] = string();
1555 pending_display[1] = string();
1559 boost::shared_ptr<AutomationControl> pc;
1560 AutomationType param;
1562 pc = available[pos];
1563 param = params[pos];
1565 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1566 _vpot->set_control (pc);
1576 pot_id = r->comp_speed_name (mc->get_value());
1597 if (!pot_id.empty()) {
1598 pending_display[0] = pot_id;
1600 pending_display[0] = string();
1603 notify_dyn_change (param, true, false);
1607 Strip::setup_eq_vpot (boost::shared_ptr<Route> r)
1609 uint32_t bands = r->eq_band_cnt ();
1612 /* should never get here */
1616 /* figure out how many params per band are available */
1618 boost::shared_ptr<AutomationControl> pc;
1619 uint32_t params_per_band = 0;
1621 if ((pc = r->eq_gain_controllable (0))) {
1622 params_per_band += 1;
1624 if ((pc = r->eq_freq_controllable (0))) {
1625 params_per_band += 1;
1627 if ((pc = r->eq_q_controllable (0))) {
1628 params_per_band += 1;
1630 if ((pc = r->eq_shape_controllable (0))) {
1631 params_per_band += 1;
1634 /* pick the one for this strip, based on its global position across
1640 const uint32_t total_band_parameters = bands * params_per_band;
1641 const uint32_t global_pos = _surface->mcp().global_index (*this);
1642 AutomationType param = NullAutomation;
1647 if (global_pos < total_band_parameters) {
1649 /* show a parameter for an EQ band */
1651 const uint32_t parameter = global_pos % params_per_band;
1652 eq_band = global_pos / params_per_band;
1653 band_name = r->eq_band_name (eq_band);
1655 switch (parameter) {
1657 pc = r->eq_gain_controllable (eq_band);
1661 pc = r->eq_freq_controllable (eq_band);
1662 param = EQFrequency;
1665 pc = r->eq_q_controllable (eq_band);
1669 pc = r->eq_shape_controllable (eq_band);
1676 /* show a non-band parameter (HPF or enable)
1679 uint32_t parameter = global_pos - total_band_parameters;
1681 switch (parameter) {
1682 case 0: /* first control after band parameters */
1683 pc = r->eq_hpf_controllable();
1686 case 1: /* second control after band parameters */
1687 pc = r->eq_enable_controllable();
1691 /* nothing to control */
1692 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1693 pending_display[0] = string();
1694 pending_display[1] = string();
1703 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1704 _vpot->set_control (pc);
1710 pot_id = band_name + "Gain";
1713 pot_id = band_name + "Freq";
1716 pot_id = band_name + " Q";
1719 pot_id = band_name + " Shp";
1731 if (!pot_id.empty()) {
1732 pending_display[0] = pot_id;
1734 pending_display[0] = string();
1737 notify_eq_change (param, eq_band, true);
1742 Strip::setup_sends_vpot (boost::shared_ptr<Route> r)
1748 const uint32_t global_pos = _surface->mcp().global_index (*this);
1750 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1753 pending_display[0] = string();
1754 pending_display[1] = string();
1758 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, BusSendLevel, global_pos, false), ui_context());
1759 _vpot->set_control (pc);
1761 pending_display[0] = PBD::short_version (r->send_name (global_pos), 6);
1763 notify_send_level_change (BusSendLevel, global_pos, true);
1767 Strip::setup_trackview_vpot (boost::shared_ptr<Route> r)
1773 const uint32_t global_pos = _surface->mcp().global_index (*this);
1775 if (global_pos >= 8) {
1776 pending_display[0] = string();
1777 pending_display[1] = string();
1781 boost::shared_ptr<AutomationControl> pc;
1782 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
1785 switch (global_pos) {
1787 pc = r->trim_control ();
1789 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, TrimAutomation, global_pos, false), ui_context());
1790 pending_display[0] = "Trim";
1791 notify_trackview_change (TrimAutomation, global_pos, true);
1796 pc = track->monitoring_control();
1798 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, MonitoringAutomation, global_pos, false), ui_context());
1799 pending_display[0] = "Mon";
1800 notify_trackview_change (MonitoringAutomation, global_pos, true);
1805 pc = r->solo_isolate_control ();
1807 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloIsolateAutomation, global_pos, false), ui_context());
1808 notify_trackview_change (SoloIsolateAutomation, global_pos, true);
1809 pending_display[0] = "S-Iso";
1813 pc = r->solo_safe_control ();
1815 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, SoloSafeAutomation, global_pos, false), ui_context());
1816 notify_trackview_change (SoloSafeAutomation, global_pos, true);
1817 pending_display[0] = "S-Safe";
1821 pc = r->phase_control();
1823 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trackview_change, this, PhaseAutomation, global_pos, false), ui_context());
1824 notify_trackview_change (PhaseAutomation, global_pos, true);
1825 pending_display[0] = "Phase";
1829 // pc = r->trim_control ();
1832 // pc = r->trim_control ();
1835 // pc = r->trim_control ();
1840 pending_display[0] = string();
1841 pending_display[1] = string();
1845 _vpot->set_control (pc);
1849 Strip::set_vpot_parameter (AutomationType p)
1851 if (!_route || (p == NullAutomation)) {
1852 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1853 pending_display[1] = string();
1857 boost::shared_ptr<AutomationControl> pan_control;
1859 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1861 reset_saved_values ();
1864 case PanAzimuthAutomation:
1865 pan_control = _route->pan_azimuth_control ();
1867 case PanWidthAutomation:
1868 pan_control = _route->pan_width_control ();
1870 case PanElevationAutomation:
1872 case PanFrontBackAutomation:
1874 case PanLFEAutomation:
1882 _vpot->set_control (pan_control);
1885 pending_display[1] = vpot_mode_string ();
1889 Strip::is_midi_track () const
1891 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1895 Strip::reset_saved_values ()
1897 _last_pan_azi_position_written = -1.0;
1898 _last_pan_width_position_written = -1.0;
1899 _last_gain_position_written = -1.0;
1900 _last_trim_position_written = -1.0;
1905 Strip::notify_metering_state_changed()
1907 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1911 if (!_route || !_meter) {
1915 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1916 bool metering_active = _surface->mcp().metering_active ();
1918 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1922 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1924 if (!transport_is_rolling || !metering_active) {
1925 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1926 notify_panner_azi_changed (true);
1929 _transport_is_rolling = transport_is_rolling;
1930 _metering_active = metering_active;