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 , vpot_parameter (PanAzimuthAutomation)
106 , _last_gain_position_written (-1.0)
107 , _last_pan_azi_position_written (-1.0)
108 , _last_pan_width_position_written (-1.0)
109 , _last_trim_position_written (-1.0)
112 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
113 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
115 if (s.mcp().device_info().has_meters()) {
116 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
119 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
120 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
121 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
122 _surface->number(), index, Button::id_to_name (bb->bid()),
123 bb->id(), b->second.base_id));
129 /* surface is responsible for deleting all controls */
133 Strip::add (Control & control)
137 Group::add (control);
139 /* fader, vpot, meter were all set explicitly */
141 if ((button = dynamic_cast<Button*>(&control)) != 0) {
142 switch (button->bid()) {
143 case Button::RecEnable:
155 case Button::VSelect:
158 case Button::FaderTouch:
159 _fader_touch = button;
168 Strip::set_route (boost::shared_ptr<Route> r, bool /*with_messages*/)
170 if (_controls_locked) {
174 mb_pan_controllable.reset();
176 route_connections.drop_connections ();
178 _solo->set_control (boost::shared_ptr<AutomationControl>());
179 _mute->set_control (boost::shared_ptr<AutomationControl>());
180 _select->set_control (boost::shared_ptr<AutomationControl>());
181 _recenable->set_control (boost::shared_ptr<AutomationControl>());
182 _fader->set_control (boost::shared_ptr<AutomationControl>());
183 _vpot->set_control (boost::shared_ptr<AutomationControl>());
187 control_by_parameter.clear ();
189 control_by_parameter[PanAzimuthAutomation] = (Control*) 0;
190 control_by_parameter[PanWidthAutomation] = (Control*) 0;
191 control_by_parameter[PanElevationAutomation] = (Control*) 0;
192 control_by_parameter[PanFrontBackAutomation] = (Control*) 0;
193 control_by_parameter[PanLFEAutomation] = (Control*) 0;
194 control_by_parameter[GainAutomation] = (Control*) 0;
195 control_by_parameter[PhaseAutomation] = (Control*) 0;
197 reset_saved_values ();
204 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 strip %2 now mapping route %3\n",
205 _surface->number(), _index, _route->name()));
207 _solo->set_control (_route->solo_control());
208 _mute->set_control (_route->mute_control());
210 _route->solo_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
211 _route->listen_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
213 _route->mute_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
215 if (_route->trim() && route()->trim()->active()) {
216 _route->trim_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_trim_changed, this, false), ui_context());
219 if (_route->phase_invert().size()) {
220 _route->phase_invert_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_phase_changed, this, false), ui_context());
221 _route->phase_control()->set_channel(0);
224 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control();
226 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
229 pan_control = _route->pan_width_control();
231 pan_control->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
234 _route->gain_control()->Changed.connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
235 _route->PropertyChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
237 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
240 _recenable->set_control (trk->rec_enable_control());
241 trk->rec_enable_control()->Changed .connect(route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_record_enable_changed, this), ui_context());
244 // TODO this works when a currently-banked route is made inactive, but not
245 // when a route is activated which should be currently banked.
247 _route->active_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_active_changed, this), ui_context());
248 _route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_route_deleted, this), ui_context());
250 /* setup legal VPot modes for this route */
252 possible_pot_parameters.clear();
254 if (_route->pan_azimuth_control()) {
255 possible_pot_parameters.push_back (PanAzimuthAutomation);
257 if (_route->pan_width_control()) {
258 possible_pot_parameters.push_back (PanWidthAutomation);
260 if (_route->pan_elevation_control()) {
261 possible_pot_parameters.push_back (PanElevationAutomation);
263 if (_route->pan_frontback_control()) {
264 possible_pot_parameters.push_back (PanFrontBackAutomation);
266 if (_route->pan_lfe_control()) {
267 possible_pot_parameters.push_back (PanLFEAutomation);
270 if (_route->phase_invert().size()) {
271 possible_trim_parameters.push_back (PhaseAutomation);
272 _route->phase_control()->set_channel(0);
276 _pan_mode = PanAzimuthAutomation;
277 potmode_changed (false);
289 // The active V-pot control may not be active for this strip
290 // But if we zero it in the controls function it may erase
291 // the one we do want
292 _surface->write (_vpot->zero());
294 notify_solo_changed ();
295 notify_mute_changed ();
296 notify_gain_changed ();
297 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
298 notify_panner_azi_changed ();
299 notify_panner_width_changed ();
300 notify_record_enable_changed ();
301 notify_trim_changed ();
302 notify_phase_changed ();
303 notify_processor_changed ();
307 Strip::notify_solo_changed ()
309 if (_route && _solo) {
310 _surface->write (_solo->set_state ((_route->soloed() || _route->listening_via_monitor()) ? on : off));
315 Strip::notify_mute_changed ()
317 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Strip %1 mute changed\n", _index));
318 if (_route && _mute) {
319 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("\troute muted ? %1\n", _route->muted()));
320 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("mute message: %1\n", _mute->set_state (_route->muted() ? on : off)));
322 _surface->write (_mute->set_state (_route->muted() ? on : off));
327 Strip::notify_record_enable_changed ()
329 if (_route && _recenable) {
330 _surface->write (_recenable->set_state (_route->record_enabled() ? on : off));
335 Strip::notify_active_changed ()
337 _surface->mcp().refresh_current_bank();
341 Strip::notify_route_deleted ()
343 _surface->mcp().refresh_current_bank();
347 Strip::notify_gain_changed (bool force_update)
353 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
359 boost::shared_ptr<AutomationControl> ac = _route->gain_control();
361 float gain_coefficient = ac->get_value();
362 float normalized_position = ac->internal_to_interface (gain_coefficient);
364 if (force_update || normalized_position != _last_gain_position_written) {
366 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
367 if (!control->in_use()) {
368 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
370 do_parameter_display (GainAutomation, gain_coefficient);
372 if (!control->in_use()) {
373 _surface->write (_fader->set_position (normalized_position));
375 do_parameter_display (GainAutomation, gain_coefficient);
378 _last_gain_position_written = normalized_position;
384 Strip::notify_trim_changed (bool force_update)
388 if (!_route->trim() || !route()->trim()->active()) {
391 Control* control = 0;
392 ControlParameterMap::iterator i = control_by_parameter.find (TrimAutomation);
394 if (i == control_by_parameter.end()) {
400 boost::shared_ptr<AutomationControl> ac = _route->trim_control();
402 float gain_coefficient = ac->get_value();
403 float normalized_position = ac->internal_to_interface (gain_coefficient);
405 if (force_update || normalized_position != _last_trim_position_written) {
406 if (control == _fader) {
407 if (!_fader->in_use()) {
408 _surface->write (_fader->set_position (normalized_position));
409 do_parameter_display (TrimAutomation, gain_coefficient);
411 } else if (control == _vpot) {
412 _surface->write (_vpot->set (normalized_position, true, Pot::dot));
413 do_parameter_display (TrimAutomation, gain_coefficient);
415 _last_trim_position_written = normalized_position;
421 Strip::notify_phase_changed (bool force_update)
424 if (!_route->phase_invert().size()) {
428 Control* control = 0;
429 ControlParameterMap::iterator i = control_by_parameter.find (PhaseAutomation);
431 if (i == control_by_parameter.end()) {
437 float normalized_position = _route->phase_control()->get_value();
439 if (control == _fader) {
440 if (!_fader->in_use()) {
441 _surface->write (_fader->set_position (normalized_position));
442 do_parameter_display (PhaseAutomation, normalized_position);
444 } else if (control == _vpot) {
445 _surface->write (_vpot->set (normalized_position, true, Pot::wrap));
446 do_parameter_display (PhaseAutomation, normalized_position);
452 Strip::notify_processor_changed (bool force_update)
457 Strip::notify_property_changed (const PropertyChange& what_changed)
459 if (!what_changed.contains (ARDOUR::Properties::name)) {
467 Strip::show_route_name ()
469 MackieControlProtocol::SubViewMode svm = _surface->mcp().subview_mode();
471 if (svm != MackieControlProtocol::None) {
472 /* subview mode is responsible for upper line */
476 string fullname = string();
478 // make sure first three strips get cleared of view mode
483 fullname = _route->name();
487 if (fullname.length() <= 6) {
490 line1 = PBD::short_version (fullname, 6);
493 pending_display[0] = line1;
497 Strip::notify_send_level_change (AutomationType type, uint32_t send_num, bool force_update)
499 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
502 /* not in subview mode */
506 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
507 /* no longer in EQ subview mode */
511 boost::shared_ptr<AutomationControl> control = r->send_level_controllable (send_num);
517 float val = control->get_value();
518 do_parameter_display (type, control->internal_to_interface (val));
519 /* update pot/encoder */
520 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
525 Strip::notify_eq_change (AutomationType type, uint32_t band, bool force_update)
527 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
530 /* not in subview mode */
534 if (_surface->mcp().subview_mode() != MackieControlProtocol::EQ) {
535 /* no longer in EQ subview mode */
539 boost::shared_ptr<AutomationControl> control;
543 control = r->eq_gain_controllable (band);
546 control = r->eq_freq_controllable (band);
549 control = r->eq_q_controllable (band);
552 control = r->eq_shape_controllable (band);
555 control = r->eq_hpf_controllable ();
558 control = r->eq_enable_controllable ();
565 float val = control->get_value();
566 do_parameter_display (type, val);
567 /* update pot/encoder */
568 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
573 Strip::notify_dyn_change (AutomationType type, bool force_update, bool propagate_mode)
575 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
578 /* not in subview mode */
582 if (_surface->mcp().subview_mode() != MackieControlProtocol::Dynamics) {
583 /* no longer in EQ subview mode */
587 boost::shared_ptr<AutomationControl> control;
588 bool reset_all = false;
592 control = r->comp_threshold_controllable ();
595 control = r->comp_speed_controllable ();
598 control = r->comp_mode_controllable ();
602 control = r->comp_makeup_controllable ();
605 control = r->comp_redux_controllable ();
608 control = r->comp_enable_controllable ();
614 if (propagate_mode && reset_all) {
615 _surface->subview_mode_changed ();
619 float val = control->get_value();
620 do_parameter_display (type, val);
621 /* update pot/encoder */
622 _surface->write (_vpot->set (control->internal_to_interface (val), true, Pot::wrap));
627 Strip::notify_panner_azi_changed (bool force_update)
633 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan change for strip %1\n", _index));
635 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
641 Control* control = 0;
642 ControlParameterMap::iterator i = control_by_parameter.find (PanAzimuthAutomation);
644 if (i == control_by_parameter.end()) {
650 double normalized_pos = pan_control->internal_to_interface (pan_control->get_value());
651 double internal_pos = pan_control->get_value();
653 if (force_update || (normalized_pos != _last_pan_azi_position_written)) {
655 if (control == _fader) {
656 if (!_fader->in_use()) {
657 _surface->write (_fader->set_position (normalized_pos));
658 /* show actual internal value to user */
659 do_parameter_display (PanAzimuthAutomation, internal_pos);
661 } else if (control == _vpot) {
662 _surface->write (_vpot->set (normalized_pos, true, Pot::dot));
663 /* show actual internal value to user */
664 do_parameter_display (PanAzimuthAutomation, internal_pos);
667 _last_pan_azi_position_written = normalized_pos;
672 Strip::notify_panner_width_changed (bool force_update)
678 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("pan width change for strip %1\n", _index));
680 boost::shared_ptr<AutomationControl> pan_control = _route->pan_width_control ();
686 Control* control = 0;
687 ControlParameterMap::iterator i = control_by_parameter.find (PanWidthAutomation);
689 if (i == control_by_parameter.end()) {
695 double pos = pan_control->internal_to_interface (pan_control->get_value());
697 if (force_update || pos != _last_pan_width_position_written) {
699 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
701 if (control == _fader) {
702 if (!control->in_use()) {
703 _surface->write (_fader->set_position (pos));
704 do_parameter_display (PanWidthAutomation, pos);
708 } else if (control == _vpot) {
709 _surface->write (_vpot->set (pos, true, Pot::spread));
710 do_parameter_display (PanWidthAutomation, pos);
713 _last_pan_width_position_written = pos;
718 Strip::select_event (Button&, ButtonState bs)
720 DEBUG_TRACE (DEBUG::MackieControl, "select button\n");
724 int ms = _surface->mcp().main_modifier_state();
726 if (ms & MackieControlProtocol::MODIFIER_CMDALT) {
727 _controls_locked = !_controls_locked;
728 _surface->write (display (1,_controls_locked ? "Locked" : "Unlock"));
729 block_vpot_mode_display_for (1000);
733 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
734 /* reset to default */
735 boost::shared_ptr<AutomationControl> ac = _fader->control ();
737 ac->set_value (ac->normal(), Controllable::NoGroup);
742 DEBUG_TRACE (DEBUG::MackieControl, "add select button on press\n");
743 _surface->mcp().add_down_select_button (_surface->number(), _index);
744 _surface->mcp().select_range ();
747 DEBUG_TRACE (DEBUG::MackieControl, "remove select button on release\n");
748 _surface->mcp().remove_down_select_button (_surface->number(), _index);
753 Strip::vselect_event (Button&, ButtonState bs)
755 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
757 /* most subview modes: vpot press acts like a button for toggle parameters */
763 if (_surface->mcp().subview_mode() != MackieControlProtocol::Sends) {
765 boost::shared_ptr<AutomationControl> control = _vpot->control ();
770 if (control->toggled()) {
771 if (control->toggled()) {
772 control->set_value (!control->get_value(), Controllable::NoGroup);
777 /* Send mode: press enables/disables the relevant send */
779 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
783 const uint32_t global_pos = _surface->mcp().global_index (*this);
784 boost::shared_ptr<AutomationControl> control = r->send_enable_controllable (global_pos);
787 bool currently_enabled = (bool) control->get_value();
788 control->set_value (!currently_enabled, Controllable::UseGroup);
790 if (currently_enabled) {
791 /* we just turned it off */
792 pending_display[1] = "off";
794 /* we just turned it on, show the level
796 control = _route->send_level_controllable (global_pos);
797 do_parameter_display (BusSendLevel, control->get_value());
803 /* done with this event in subview mode */
810 int ms = _surface->mcp().main_modifier_state();
812 if (ms & MackieControlProtocol::MODIFIER_SHIFT) {
814 boost::shared_ptr<AutomationControl> ac = _vpot->control ();
818 /* reset to default/normal value */
819 ac->set_value (ac->normal(), Controllable::NoGroup);
826 boost::shared_ptr<AutomationControl> ac = _route->master_send_enable_controllable ();
828 bool enabled = ac->get_value();
829 ac->set_value (!enabled, Controllable::UseGroup);
832 DEBUG_TRACE (DEBUG::MackieControl, "switching to next pot mode\n");
841 Strip::fader_touch_event (Button&, ButtonState bs)
843 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader touch, press ? %1\n", (bs == press)));
847 boost::shared_ptr<AutomationControl> ac = _fader->control ();
849 _fader->set_in_use (true);
850 _fader->start_touch (_surface->mcp().transport_frame());
853 do_parameter_display ((AutomationType) ac->parameter().type(), ac->get_value());
858 _fader->set_in_use (false);
859 _fader->stop_touch (_surface->mcp().transport_frame(), true);
866 Strip::handle_button (Button& button, ButtonState bs)
868 boost::shared_ptr<AutomationControl> control;
871 button.set_in_use (true);
873 button.set_in_use (false);
876 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
878 switch (button.bid()) {
880 select_event (button, bs);
883 case Button::VSelect:
884 vselect_event (button, bs);
887 case Button::FaderTouch:
888 fader_touch_event (button, bs);
892 if ((control = button.control ())) {
894 DEBUG_TRACE (DEBUG::MackieControl, "add button on press\n");
895 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
897 float new_value = control->get_value() ? 0.0 : 1.0;
899 /* get all controls that either have their
900 * button down or are within a range of
901 * several down buttons
904 MackieControlProtocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type());
907 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
908 controls.size(), control->parameter().type(), new_value));
910 /* apply change, with potential modifier semantics */
912 Controllable::GroupControlDisposition gcd;
914 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
915 gcd = Controllable::NoGroup;
917 gcd = Controllable::UseGroup;
920 for (MackieControlProtocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
921 (*c)->set_value (new_value, gcd);
925 DEBUG_TRACE (DEBUG::MackieControl, "remove button on release\n");
926 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
934 Strip::do_parameter_display (AutomationType type, float val)
936 bool screen_hold = false;
942 pending_display[1] = " -inf ";
944 float dB = accurate_coefficient_to_dB (val);
945 snprintf (buf, sizeof (buf), "%6.1f", dB);
946 pending_display[1] = buf;
951 case PanAzimuthAutomation:
952 if (Profile->get_mixbus()) {
953 snprintf (buf, sizeof (buf), "%2.1f", val);
954 pending_display[1] = buf;
958 boost::shared_ptr<Pannable> p = _route->pannable();
959 if (p && _route->panner()) {
960 pending_display[1] =_route->panner()->value_as_string (p->pan_azimuth_control);
967 case PanWidthAutomation:
969 snprintf (buf, sizeof (buf), "%5ld%%", lrintf ((val * 200.0)-100));
970 pending_display[1] = buf;
977 float dB = accurate_coefficient_to_dB (val);
978 snprintf (buf, sizeof (buf), "%6.1f", dB);
979 pending_display[1] = buf;
984 case PhaseAutomation:
986 if (_route->phase_control()->get_value() < 0.5) {
987 pending_display[1] = "Normal";
989 pending_display[1] = "Invert";
997 float dB = accurate_coefficient_to_dB (val);
998 snprintf (buf, sizeof (buf), "%6.1f", dB);
999 pending_display[1] = buf;
1013 snprintf (buf, sizeof (buf), "%6.1f", val);
1014 pending_display[1] = buf;
1020 pending_display[1] = "on";
1022 pending_display[1] = "off";
1026 if (_surface->mcp().subview_route()) {
1027 pending_display[1] = _surface->mcp().subview_route()->comp_mode_name (val);
1035 /* we just queued up a parameter to be displayed.
1036 1 second from now, switch back to vpot mode display.
1038 block_vpot_mode_display_for (1000);
1043 Strip::handle_fader_touch (Fader& fader, bool touch_on)
1046 fader.start_touch (_surface->mcp().transport_frame());
1048 fader.stop_touch (_surface->mcp().transport_frame(), false);
1053 Strip::handle_fader (Fader& fader, float position)
1055 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
1056 boost::shared_ptr<AutomationControl> ac = fader.control();
1061 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
1063 if (_surface->mcp().main_modifier_state() & MackieControlProtocol::MODIFIER_SHIFT) {
1064 gcd = Controllable::NoGroup;
1067 fader.set_value (position, gcd);
1069 /* From the Mackie Control MIDI implementation docs:
1071 In order to ensure absolute synchronization with the host software,
1072 Mackie Control uses a closed-loop servo system for the faders,
1073 meaning the faders will always move to their last received position.
1074 When a host receives a Fader Position Message, it must then
1075 re-transmit that message to the Mackie Control or else the faders
1076 will return to their last position.
1079 _surface->write (fader.set_position (position));
1083 Strip::handle_pot (Pot& pot, float delta)
1085 /* Pots only emit events when they move, not when they
1086 stop moving. So to get a stop event, we need to use a timeout.
1089 boost::shared_ptr<AutomationControl> ac = pot.control();
1093 double p = pot.get_value ();
1095 // fader and pot should be the same and fader is hard coded 0 -> 1
1102 Strip::periodic (ARDOUR::microseconds_t now)
1105 update_automation ();
1109 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
1111 if (_block_screen_redisplay_until >= now) {
1112 /* no drawing allowed */
1116 if (_block_screen_redisplay_until) {
1117 /* we were blocked, but the time period has elapsed, so we must
1121 _block_screen_redisplay_until = 0;
1124 if (force || (current_display[0] != pending_display[0])) {
1125 _surface->write (display (0, pending_display[0]));
1126 current_display[0] = pending_display[0];
1129 if (return_to_vpot_mode_display_at <= now) {
1130 return_to_vpot_mode_display_at = UINT64_MAX;
1131 return_to_vpot_mode_display ();
1134 if (force || (current_display[1] != pending_display[1])) {
1135 _surface->write (display (1, pending_display[1]));
1136 current_display[1] = pending_display[1];
1141 Strip::update_automation ()
1147 ARDOUR::AutoState state = _route->gain_control()->automation_state();
1149 if (state == Touch || state == Play) {
1150 notify_gain_changed (false);
1153 boost::shared_ptr<AutomationControl> pan_control = _route->pan_azimuth_control ();
1155 state = pan_control->automation_state ();
1156 if (state == Touch || state == Play) {
1157 notify_panner_azi_changed (false);
1161 pan_control = _route->pan_width_control ();
1163 state = pan_control->automation_state ();
1164 if (state == Touch || state == Play) {
1165 notify_panner_width_changed (false);
1169 if (_route->trim() && route()->trim()->active()) {
1170 ARDOUR::AutoState trim_state = _route->trim_control()->automation_state();
1171 if (trim_state == Touch || trim_state == Play) {
1172 notify_trim_changed (false);
1178 Strip::update_meter ()
1184 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1188 if (_meter && _transport_is_rolling && _metering_active) {
1189 float dB = const_cast<PeakMeter&> (_route->peak_meter()).meter_level (0, MeterMCP);
1190 _meter->send_update (*_surface, dB);
1198 for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
1199 _surface->write ((*it)->zero ());
1202 _surface->write (blank_display (0));
1203 _surface->write (blank_display (1));
1204 pending_display[0] = string();
1205 pending_display[1] = string();
1206 current_display[0] = string();
1207 current_display[1] = string();
1211 Strip::blank_display (uint32_t line_number)
1213 return display (line_number, string());
1217 Strip::display (uint32_t line_number, const std::string& line)
1219 assert (line_number <= 1);
1221 MidiByteArray retval;
1223 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
1226 retval << _surface->sysex_hdr();
1230 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
1231 retval << (_index * 7 + (line_number * 0x38));
1233 // ascii data to display. @param line is UTF-8
1234 string ascii = Glib::convert_with_fallback (line, "UTF-8", "ISO-8859-1", "_");
1235 string::size_type len = ascii.length();
1237 ascii = ascii.substr (0, 6);
1241 // pad with " " out to 6 chars
1242 for (int i = len; i < 6; ++i) {
1246 // column spacer, unless it's the right-hand column
1252 retval << MIDI::eox;
1258 Strip::lock_controls ()
1260 _controls_locked = true;
1264 Strip::unlock_controls ()
1266 _controls_locked = false;
1270 Strip::gui_selection_changed (const ARDOUR::StrongRouteNotificationList& rl)
1272 for (ARDOUR::StrongRouteNotificationList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
1273 if ((*i) == _route) {
1274 _surface->write (_select->set_state (on));
1279 _surface->write (_select->set_state (off));
1283 Strip::vpot_mode_string ()
1285 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1290 if (control_by_parameter.find (GainAutomation)->second == _vpot) {
1292 } else if (control_by_parameter.find (TrimAutomation)->second == _vpot) {
1294 } else if (control_by_parameter.find (PhaseAutomation)->second == _vpot) {
1295 return string_compose ("Phase%1", _route->phase_control()->channel() + 1);
1296 } else if (control_by_parameter.find (PanAzimuthAutomation)->second == _vpot) {
1298 } else if (control_by_parameter.find (PanWidthAutomation)->second == _vpot) {
1300 } else if (control_by_parameter.find (PanElevationAutomation)->second == _vpot) {
1302 } else if (control_by_parameter.find (PanFrontBackAutomation)->second == _vpot) {
1304 } else if (control_by_parameter.find (PanLFEAutomation)->second == _vpot) {
1308 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1316 Strip::potmode_changed (bool notify)
1323 int pm = _surface->mcp().pot_mode();
1325 case MackieControlProtocol::Pan:
1326 // This needs to set current pan mode (azimuth or width... or whatever)
1327 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Assign pot to Pan mode %1\n", enum_2_string (_pan_mode)));
1328 set_vpot_parameter (_pan_mode);
1338 Strip::block_screen_display_for (uint32_t msecs)
1340 _block_screen_redisplay_until = ARDOUR::get_microseconds() + (msecs * 1000);
1344 Strip::block_vpot_mode_display_for (uint32_t msecs)
1346 return_to_vpot_mode_display_at = ARDOUR::get_microseconds() + (msecs * 1000);
1350 Strip::return_to_vpot_mode_display ()
1352 /* returns the second line of the two-line per-strip display
1353 back the mode where it shows what the VPot controls.
1356 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1357 /* do nothing - second line shows value of current subview parameter */
1359 } else if (_route) {
1360 pending_display[1] = vpot_mode_string();
1362 pending_display[1] = string();
1367 Strip::next_pot_mode ()
1369 vector<AutomationType>::iterator i;
1371 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1372 /* do not change vpot mode while in flipped mode */
1373 DEBUG_TRACE (DEBUG::MackieControl, "not stepping pot mode - in flip mode\n");
1374 pending_display[1] = "Flip";
1375 block_vpot_mode_display_for (1000);
1380 boost::shared_ptr<AutomationControl> ac = _vpot->control();
1387 if (_surface->mcp().pot_mode() == MackieControlProtocol::Pan) {
1389 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
1393 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
1394 if ((*i) == ac->parameter().type()) {
1399 /* move to the next mode in the list, or back to the start (which will
1400 also happen if the current mode is not in the current pot mode list)
1403 if (i != possible_pot_parameters.end()) {
1407 if (i == possible_pot_parameters.end()) {
1408 i = possible_pot_parameters.begin();
1411 set_vpot_parameter (*i);
1416 Strip::subview_mode_changed ()
1418 boost::shared_ptr<Route> r = _surface->mcp().subview_route();
1420 subview_connections.drop_connections ();
1422 switch (_surface->mcp().subview_mode()) {
1423 case MackieControlProtocol::None:
1424 set_vpot_parameter (vpot_parameter);
1425 /* need to show strip name again */
1427 notify_metering_state_changed ();
1431 case MackieControlProtocol::EQ:
1435 /* leave it as it was */
1439 case MackieControlProtocol::Dynamics:
1443 /* leave it as it was */
1448 case MackieControlProtocol::Sends:
1450 setup_sends_vpot (r);
1452 /* leave it as it was */
1456 case MackieControlProtocol::TrackView:
1458 setup_trackview_vpot (r);
1460 /* leave it as it was */
1468 Strip::setup_trackview_vpot (boost::shared_ptr<Route> r)
1473 Strip::setup_dyn_vpot (boost::shared_ptr<Route> r)
1479 boost::shared_ptr<AutomationControl> tc = r->comp_threshold_controllable ();
1480 boost::shared_ptr<AutomationControl> sc = r->comp_speed_controllable ();
1481 boost::shared_ptr<AutomationControl> mc = r->comp_mode_controllable ();
1482 boost::shared_ptr<AutomationControl> kc = r->comp_makeup_controllable ();
1483 boost::shared_ptr<AutomationControl> rc = r->comp_redux_controllable ();
1484 boost::shared_ptr<AutomationControl> ec = r->comp_enable_controllable ();
1486 uint32_t pos = _surface->mcp().global_index (*this);
1488 /* we will control the pos-th available parameter, from the list in the
1489 * order shown above.
1492 vector<boost::shared_ptr<AutomationControl> > available;
1493 vector<AutomationType> params;
1495 if (tc) { available.push_back (tc); params.push_back (CompThreshold); }
1496 if (sc) { available.push_back (sc); params.push_back (CompSpeed); }
1497 if (mc) { available.push_back (mc); params.push_back (CompMode); }
1498 if (kc) { available.push_back (kc); params.push_back (CompMakeup); }
1499 if (rc) { available.push_back (rc); params.push_back (CompRedux); }
1500 if (ec) { available.push_back (ec); params.push_back (CompEnable); }
1502 if (pos >= available.size()) {
1503 /* this knob is not needed to control the available parameters */
1504 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1505 pending_display[0] = string();
1506 pending_display[1] = string();
1510 boost::shared_ptr<AutomationControl> pc;
1511 AutomationType param;
1513 pc = available[pos];
1514 param = params[pos];
1516 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_dyn_change, this, param, false, true), ui_context());
1517 _vpot->set_control (pc);
1527 pot_id = r->comp_speed_name (mc->get_value());
1548 if (!pot_id.empty()) {
1549 pending_display[0] = pot_id;
1551 pending_display[0] = string();
1554 notify_dyn_change (param, true, false);
1558 Strip::setup_eq_vpot (boost::shared_ptr<Route> r)
1560 uint32_t bands = r->eq_band_cnt ();
1563 /* should never get here */
1567 /* figure out how many params per band are available */
1569 boost::shared_ptr<AutomationControl> pc;
1570 uint32_t params_per_band = 0;
1572 if ((pc = r->eq_gain_controllable (0))) {
1573 params_per_band += 1;
1575 if ((pc = r->eq_freq_controllable (0))) {
1576 params_per_band += 1;
1578 if ((pc = r->eq_q_controllable (0))) {
1579 params_per_band += 1;
1581 if ((pc = r->eq_shape_controllable (0))) {
1582 params_per_band += 1;
1585 /* pick the one for this strip, based on its global position across
1591 const uint32_t total_band_parameters = bands * params_per_band;
1592 const uint32_t global_pos = _surface->mcp().global_index (*this);
1593 AutomationType param = NullAutomation;
1598 if (global_pos < total_band_parameters) {
1600 /* show a parameter for an EQ band */
1602 const uint32_t parameter = global_pos % params_per_band;
1603 eq_band = global_pos / params_per_band;
1604 band_name = r->eq_band_name (eq_band);
1606 switch (parameter) {
1608 pc = r->eq_gain_controllable (eq_band);
1612 pc = r->eq_freq_controllable (eq_band);
1613 param = EQFrequency;
1616 pc = r->eq_q_controllable (eq_band);
1620 pc = r->eq_shape_controllable (eq_band);
1627 /* show a non-band parameter (HPF or enable)
1630 uint32_t parameter = global_pos - total_band_parameters;
1632 switch (parameter) {
1633 case 0: /* first control after band parameters */
1634 pc = r->eq_hpf_controllable();
1637 case 1: /* second control after band parameters */
1638 pc = r->eq_enable_controllable();
1642 /* nothing to control */
1643 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1644 pending_display[0] = string();
1645 pending_display[1] = string();
1654 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_eq_change, this, param, eq_band, false), ui_context());
1655 _vpot->set_control (pc);
1661 pot_id = band_name + "Gain";
1664 pot_id = band_name + "Freq";
1667 pot_id = band_name + " Q";
1670 pot_id = band_name + " Shp";
1682 if (!pot_id.empty()) {
1683 pending_display[0] = pot_id;
1685 pending_display[0] = string();
1688 notify_eq_change (param, eq_band, true);
1693 Strip::setup_sends_vpot (boost::shared_ptr<Route> r)
1699 const uint32_t global_pos = _surface->mcp().global_index (*this);
1701 boost::shared_ptr<AutomationControl> pc = r->send_level_controllable (global_pos);
1704 pending_display[0] = string();
1705 pending_display[1] = string();
1709 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_send_level_change, this, BusSendLevel, global_pos, false), ui_context());
1710 _vpot->set_control (pc);
1712 pending_display[0] = r->send_name (global_pos);
1714 notify_send_level_change (BusSendLevel, global_pos, true);
1718 Strip::set_vpot_parameter (AutomationType p)
1720 if (!_route || (p == NullAutomation)) {
1721 control_by_parameter[vpot_parameter] = 0;
1722 vpot_parameter = NullAutomation;
1723 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1724 pending_display[1] = string();
1728 boost::shared_ptr<AutomationControl> pan_control;
1730 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("switch to vpot mode %1\n", p));
1732 reset_saved_values ();
1734 /* unset any mapping between the vpot and any existing parameters */
1736 for (ControlParameterMap::iterator i = control_by_parameter.begin(); i != control_by_parameter.end(); ++i) {
1738 if (i != control_by_parameter.end() && i->second == _vpot) {
1744 case PanAzimuthAutomation:
1745 if ((pan_control = _route->pan_azimuth_control ())) {
1746 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1747 _pan_mode = PanAzimuthAutomation;
1748 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1749 /* gain to vpot, pan azi to fader */
1750 _vpot->set_control (_route->gain_control());
1751 vpot_parameter = GainAutomation;
1752 control_by_parameter[GainAutomation] = _vpot;
1753 _fader->set_control (pan_control);
1754 control_by_parameter[PanAzimuthAutomation] = _fader;
1756 _fader->set_control (boost::shared_ptr<AutomationControl>());
1757 control_by_parameter[PanAzimuthAutomation] = 0;
1760 /* gain to fader, pan azi to vpot */
1761 vpot_parameter = PanAzimuthAutomation;
1762 _fader->set_control (_route->gain_control());
1763 control_by_parameter[GainAutomation] = _fader;
1764 _vpot->set_control (pan_control);
1765 control_by_parameter[PanAzimuthAutomation] = _vpot;
1768 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1769 control_by_parameter[PanAzimuthAutomation] = 0;
1773 case PanWidthAutomation:
1774 if ((pan_control = _route->pan_width_control ())) {
1775 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1776 _pan_mode = PanWidthAutomation;
1777 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1778 /* gain to vpot, pan width to fader */
1779 _vpot->set_control (_route->gain_control());
1780 vpot_parameter = GainAutomation;
1781 control_by_parameter[GainAutomation] = _vpot;
1782 _fader->set_control (pan_control);
1783 control_by_parameter[PanWidthAutomation] = _fader;
1785 _fader->set_control (boost::shared_ptr<AutomationControl>());
1786 control_by_parameter[PanWidthAutomation] = 0;
1789 /* gain to fader, pan width to vpot */
1790 vpot_parameter = PanWidthAutomation;
1791 _fader->set_control (_route->gain_control());
1792 control_by_parameter[GainAutomation] = _fader;
1793 _vpot->set_control (pan_control);
1794 control_by_parameter[PanWidthAutomation] = _vpot;
1797 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1798 control_by_parameter[PanWidthAutomation] = 0;
1802 case PanElevationAutomation:
1804 case PanFrontBackAutomation:
1806 case PanLFEAutomation:
1808 case TrimAutomation:
1809 _trim_mode = TrimAutomation;
1810 vpot_parameter = TrimAutomation;
1811 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1812 /* gain to vpot, trim to fader */
1813 _vpot->set_control (_route->gain_control());
1814 control_by_parameter[GainAutomation] = _vpot;
1815 if (_route->trim() && route()->trim()->active()) {
1816 _fader->set_control (_route->trim_control());
1817 control_by_parameter[TrimAutomation] = _fader;
1819 _fader->set_control (boost::shared_ptr<AutomationControl>());
1820 control_by_parameter[TrimAutomation] = 0;
1823 /* gain to fader, trim to vpot */
1824 _fader->set_control (_route->gain_control());
1825 control_by_parameter[GainAutomation] = _fader;
1826 if (_route->trim() && route()->trim()->active()) {
1827 _vpot->set_control (_route->trim_control());
1828 control_by_parameter[TrimAutomation] = _vpot;
1830 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1831 control_by_parameter[TrimAutomation] = 0;
1835 case PhaseAutomation:
1836 _trim_mode = PhaseAutomation;
1837 vpot_parameter = PhaseAutomation;
1838 if (_surface->mcp().flip_mode() != MackieControlProtocol::Normal) {
1839 /* gain to vpot, phase to fader */
1840 _vpot->set_control (_route->gain_control());
1841 control_by_parameter[GainAutomation] = _vpot;
1842 if (_route->phase_invert().size()) {
1843 _fader->set_control (_route->phase_control());
1844 control_by_parameter[PhaseAutomation] = _fader;
1846 _fader->set_control (boost::shared_ptr<AutomationControl>());
1847 control_by_parameter[PhaseAutomation] = 0;
1850 /* gain to fader, phase to vpot */
1851 _fader->set_control (_route->gain_control());
1852 control_by_parameter[GainAutomation] = _fader;
1853 if (_route->phase_invert().size()) {
1854 _vpot->set_control (_route->phase_control());
1855 control_by_parameter[PhaseAutomation] = _vpot;
1857 _vpot->set_control (boost::shared_ptr<AutomationControl>());
1858 control_by_parameter[PhaseAutomation] = 0;
1863 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("vpot mode %1 not known.\n", p));
1868 pending_display[1] = vpot_mode_string ();
1872 Strip::is_midi_track () const
1874 return boost::dynamic_pointer_cast<MidiTrack>(_route) != 0;
1878 Strip::reset_saved_values ()
1880 _last_pan_azi_position_written = -1.0;
1881 _last_pan_width_position_written = -1.0;
1882 _last_gain_position_written = -1.0;
1883 _last_trim_position_written = -1.0;
1888 Strip::notify_metering_state_changed()
1890 if (_surface->mcp().subview_mode() != MackieControlProtocol::None) {
1894 if (!_route || !_meter) {
1898 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1899 bool metering_active = _surface->mcp().metering_active ();
1901 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1905 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1907 if (!transport_is_rolling || !metering_active) {
1908 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1909 notify_panner_azi_changed (true);
1912 _transport_is_rolling = transport_is_rolling;
1913 _metering_active = metering_active;