2 Copyright (C) 2006,2007 John Anderson
3 Copyright (C) 2012 Paul Davis
4 Copyright (C) 2017 Ben Loftis
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <glibmm/convert.h>
31 #include "midi++/port.h"
33 #include "pbd/compose.h"
34 #include "pbd/convert.h"
36 #include "ardour/amp.h"
37 #include "ardour/bundle.h"
38 #include "ardour/debug.h"
39 #include "ardour/midi_ui.h"
40 #include "ardour/meter.h"
41 #include "ardour/monitor_control.h"
42 #include "ardour/plugin_insert.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/phase_control.h"
47 #include "ardour/rc_configuration.h"
48 #include "ardour/record_enable_control.h"
49 #include "ardour/route.h"
50 #include "ardour/session.h"
51 #include "ardour/send.h"
52 #include "ardour/solo_isolate_control.h"
53 #include "ardour/track.h"
54 #include "ardour/midi_track.h"
55 #include "ardour/user_bundle.h"
56 #include "ardour/profile.h"
57 #include "ardour/value_as_string.h"
59 #include "us2400_control_protocol.h"
60 #include "surface_port.h"
71 using namespace ARDOUR;
73 using namespace ArdourSurface;
74 using namespace US2400;
76 #ifndef timeradd /// only avail with __USE_BSD
77 #define timeradd(a,b,result) \
79 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
80 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
81 if ((result)->tv_usec >= 1000000) \
84 (result)->tv_usec -= 1000000; \
89 #define ui_context() US2400Protocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
91 Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
102 , _controls_locked (false)
103 , _transport_is_rolling (false)
104 , _metering_active (true)
105 , _pan_mode (PanAzimuthAutomation)
107 _fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
108 _vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
110 if (s.mcp().device_info().has_meters()) {
111 _meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
114 for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
115 Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
116 DEBUG_TRACE (DEBUG::US2400, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
117 _surface->number(), index, Button::id_to_name (bb->bid()),
118 bb->id(), b->second.base_id));
121 _trickle_counter = 0;
126 /* surface is responsible for deleting all controls */
130 Strip::add (Control & control)
134 Group::add (control);
136 /* fader, vpot, meter were all set explicitly */
138 if ((button = dynamic_cast<Button*>(&control)) != 0) {
139 switch (button->bid()) {
149 case Button::FaderTouch:
150 _fader_touch = button;
159 Strip::set_stripable (boost::shared_ptr<Stripable> r, bool /*with_messages*/)
161 if (_controls_locked) {
165 stripable_connections.drop_connections ();
167 _solo->set_control (boost::shared_ptr<AutomationControl>());
168 _mute->set_control (boost::shared_ptr<AutomationControl>());
169 _select->set_control (boost::shared_ptr<AutomationControl>());
171 _fader->set_control (boost::shared_ptr<AutomationControl>());
172 _vpot->set_control (boost::shared_ptr<AutomationControl>());
179 DEBUG_TRACE (DEBUG::US2400, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
184 DEBUG_TRACE (DEBUG::US2400, string_compose ("Surface %1 strip %2 now mapping stripable %3\n",
185 _surface->number(), _index, _stripable->name()));
187 _solo->set_control (_stripable->solo_control());
188 _mute->set_control (_stripable->mute_control());
190 _stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
191 _stripable->mute_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
193 boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control();
195 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
198 pan_control = _stripable->pan_width_control();
200 pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
203 _stripable->gain_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
204 _stripable->PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
205 _stripable->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
207 // TODO this works when a currently-banked stripable is made inactive, but not
208 // when a stripable is activated which should be currently banked.
210 _stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_stripable_deleted, this), ui_context());
212 /* setup legal VPot modes for this stripable */
214 possible_pot_parameters.clear();
216 if (_stripable->pan_azimuth_control()) {
217 possible_pot_parameters.push_back (PanAzimuthAutomation);
219 if (_stripable->pan_width_control()) {
220 possible_pot_parameters.push_back (PanWidthAutomation);
222 if (_stripable->pan_elevation_control()) {
223 possible_pot_parameters.push_back (PanElevationAutomation);
225 if (_stripable->pan_frontback_control()) {
226 possible_pot_parameters.push_back (PanFrontBackAutomation);
228 if (_stripable->pan_lfe_control()) {
229 possible_pot_parameters.push_back (PanLFEAutomation);
232 _pan_mode = PanAzimuthAutomation;
234 if (_surface->mcp().subview_mode() == US2400Protocol::None) {
235 set_vpot_parameter (_pan_mode);
238 _fader->set_control (_stripable->gain_control());
244 Strip::reset_stripable ()
246 stripable_connections.drop_connections ();
248 _solo->set_control (boost::shared_ptr<AutomationControl>());
249 _mute->set_control (boost::shared_ptr<AutomationControl>());
250 _select->set_control (boost::shared_ptr<AutomationControl>());
252 _fader->reset_control ();
253 _vpot->reset_control ();
272 // The active V-pot control may not be active for this strip
273 // But if we zero it in the controls function it may erase
274 // the one we do want
276 _surface->write (_vpot->zero());
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_vpot_change ();
286 notify_panner_width_changed ();
287 notify_record_enable_changed ();
289 notify_processor_changed ();
294 Strip::notify_solo_changed ()
297 if (_stripable && _solo) {
298 _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
302 _solo->mark_dirty ();
303 _trickle_counter = 0;
307 Strip::notify_mute_changed ()
309 DEBUG_TRACE (DEBUG::US2400, string_compose ("Strip %1 mute changed\n", _index));
311 if (_stripable && _mute) {
312 DEBUG_TRACE (DEBUG::US2400, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
313 DEBUG_TRACE (DEBUG::US2400, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
315 _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
317 _surface->write (_mute->zero());
321 _mute->mark_dirty ();
322 _trickle_counter = 0;
326 Strip::notify_record_enable_changed ()
331 Strip::notify_stripable_deleted ()
333 _surface->mcp().notify_stripable_removed ();
334 _surface->mcp().refresh_current_bank();
338 Strip::notify_gain_changed (bool force_update)
340 _fader->mark_dirty();
341 _trickle_counter = 0;
345 Strip::notify_processor_changed (bool force_update)
350 Strip::notify_property_changed (const PropertyChange& what_changed)
355 Strip::update_selection_state ()
357 _select->mark_dirty ();
358 _trickle_counter = 0;
361 _surface->write (_select->set_state (_stripable->is_selected()));
367 Strip::show_stripable_name ()
372 Strip::notify_vpot_change ()
375 _trickle_counter = 0;
379 Strip::notify_panner_azi_changed (bool force_update)
382 _trickle_counter = 0;
386 Strip::notify_panner_width_changed (bool force_update)
388 _trickle_counter = 0;
392 Strip::select_event (Button&, ButtonState bs)
394 DEBUG_TRACE (DEBUG::US2400, "select button\n");
398 int ms = _surface->mcp().main_modifier_state();
400 if (ms & US2400Protocol::MODIFIER_CMDALT) {
401 _controls_locked = !_controls_locked;
405 DEBUG_TRACE (DEBUG::US2400, "add select button on press\n");
406 _surface->mcp().add_down_select_button (_surface->number(), _index);
407 _surface->mcp().select_range (_surface->mcp().global_index (*this));
410 DEBUG_TRACE (DEBUG::US2400, "remove select button on release\n");
411 _surface->mcp().remove_down_select_button (_surface->number(), _index);
414 _trickle_counter = 0;
418 Strip::vselect_event (Button&, ButtonState bs)
423 Strip::fader_touch_event (Button&, ButtonState bs)
425 DEBUG_TRACE (DEBUG::US2400, string_compose ("fader touch, press ? %1\n", (bs == press)));
429 boost::shared_ptr<AutomationControl> ac = _fader->control ();
431 _fader->set_in_use (true);
432 _fader->start_touch (_surface->mcp().transport_frame());
436 _fader->set_in_use (false);
437 _fader->stop_touch (_surface->mcp().transport_frame());
444 Strip::handle_button (Button& button, ButtonState bs)
446 boost::shared_ptr<AutomationControl> control;
449 button.set_in_use (true);
451 button.set_in_use (false);
454 DEBUG_TRACE (DEBUG::US2400, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
456 switch (button.bid()) {
458 select_event (button, bs);
461 case Button::FaderTouch:
462 fader_touch_event (button, bs);
466 if ((control = button.control ())) {
468 DEBUG_TRACE (DEBUG::US2400, "add button on press\n");
469 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
471 float new_value = control->get_value() ? 0.0 : 1.0;
473 /* get all controls that either have their
474 * button down or are within a range of
475 * several down buttons
478 US2400Protocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
479 _surface->mcp().global_index(*this));
482 DEBUG_TRACE (DEBUG::US2400, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
483 controls.size(), control->parameter().type(), new_value));
485 /* apply change, with potential modifier semantics */
487 Controllable::GroupControlDisposition gcd;
489 if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
490 gcd = Controllable::InverseGroup;
492 gcd = Controllable::UseGroup;
495 for (US2400Protocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
496 (*c)->set_value (new_value, gcd);
500 DEBUG_TRACE (DEBUG::US2400, "remove button on release\n");
501 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
510 Strip::handle_fader_touch (Fader& fader, bool touch_on)
513 fader.start_touch (_surface->mcp().transport_frame());
515 fader.stop_touch (_surface->mcp().transport_frame());
520 Strip::handle_fader (Fader& fader, float position)
522 DEBUG_TRACE (DEBUG::US2400, string_compose ("fader to %1\n", position));
523 boost::shared_ptr<AutomationControl> ac = fader.control();
528 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
530 if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
531 gcd = Controllable::InverseGroup;
534 fader.set_value (position, gcd);
536 /* From the Mackie Control MIDI implementation docs:
538 In order to ensure absolute synchronization with the host software,
539 Mackie Control uses a closed-loop servo system for the faders,
540 meaning the faders will always move to their last received position.
541 When a host receives a Fader Position Message, it must then
542 re-transmit that message to the Mackie Control or else the faders
543 will return to their last position.
546 _surface->write (fader.set_position (position));
550 Strip::handle_pot (Pot& pot, float delta)
552 /* Pots only emit events when they move, not when they
553 stop moving. So to get a stop event, we need to use a timeout.
556 boost::shared_ptr<AutomationControl> ac = pot.control();
561 Controllable::GroupControlDisposition gcd;
563 if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
564 gcd = Controllable::InverseGroup;
566 gcd = Controllable::UseGroup;
571 /* make it like a single-step, directional switch */
574 ac->set_value (1.0, gcd);
576 ac->set_value (0.0, gcd);
579 } else if (ac->desc().enumeration || ac->desc().integer_step) {
581 /* use Controllable::get_value() to avoid the
582 * "scaling-to-interface" that takes place in
583 * Control::get_value() via the pot member.
585 * an enumeration with 4 values will have interface values of
586 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
591 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
593 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
598 double p = ac->get_interface();
605 ac->set_value ( ac->interface_to_internal(p), gcd);
610 Strip::periodic (ARDOUR::microseconds_t now)
615 if ( _trickle_counter %24 == 0 ) {
617 if ( _fader->control() ) {
618 _surface->write (_fader->set_position (_fader->control()->internal_to_interface (_fader->control()->get_value ())));
620 _surface->write (_fader->set_position(0.0));
623 if ( _vpot->control() ) {
624 _surface->write (_vpot->set (_vpot->control()->internal_to_interface (_vpot->control()->get_value ()), true));
626 _surface->write (_vpot->set(0.0, false));
630 _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
631 _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
632 _surface->write (_select->set_state (_stripable->is_selected()));
634 _surface->write (_solo->set_state (off));
635 _surface->write (_mute->set_state (off));
636 _surface->write (_select->set_state (off));
641 //after a hard write, queue us for trickling data later
642 if (_trickle_counter == 0)
643 _trickle_counter = global_index()+1;
650 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
655 Strip::update_automation ()
660 Strip::update_meter ()
666 if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
667 float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
668 _meter->send_update (*_surface, dB);
676 _trickle_counter = 0;
680 Strip::lock_controls ()
682 _controls_locked = true;
686 Strip::unlock_controls ()
688 _controls_locked = false;
692 Strip::vpot_mode_string ()
698 Strip::next_pot_mode ()
700 vector<AutomationType>::iterator i;
702 boost::shared_ptr<AutomationControl> ac = _vpot->control();
709 if (_surface->mcp().subview_mode() != US2400Protocol::None) {
713 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
717 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
718 if ((*i) == ac->parameter().type()) {
723 /* move to the next mode in the list, or back to the start (which will
724 also happen if the current mode is not in the current pot mode list)
727 if (i != possible_pot_parameters.end()) {
731 if (i == possible_pot_parameters.end()) {
732 i = possible_pot_parameters.begin();
735 set_vpot_parameter (*i);
741 * name: Strip::subview_mode_changed
746 Strip::subview_mode_changed ()
748 switch (_surface->mcp().subview_mode()) {
750 case US2400Protocol::None:
751 set_vpot_parameter (_pan_mode);
752 notify_metering_state_changed ();
755 case US2400Protocol::TrackView:
756 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
758 DEBUG_TRACE (DEBUG::US2400, string_compose("subview_mode_changed strip %1:%2- assigning trackview pot\n", _surface->number(), _index));
759 setup_trackview_vpot (r);
761 DEBUG_TRACE (DEBUG::US2400, string_compose("subview_mode_changed strip %1:%2 - no stripable\n", _surface->number(), _index));
767 _trickle_counter = 0;
771 Strip::setup_dyn_vpot (boost::shared_ptr<Stripable> r)
776 Strip::setup_eq_vpot (boost::shared_ptr<Stripable> r)
781 Strip::setup_sends_vpot (boost::shared_ptr<Stripable> r)
787 Strip::setup_trackview_vpot (boost::shared_ptr<Stripable> r)
789 subview_connections.drop_connections ();
796 boost::shared_ptr<AutomationControl> pc;
797 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
800 _vpot->set_mode(Pot::wrap);
803 const uint32_t global_pos = _surface->mcp().global_index (*this);
806 switch (global_pos) {
808 pc = r->trim_control ();
809 _vpot->set_mode(Pot::boost_cut);
813 pc = r->pan_azimuth_control ();
814 _vpot->set_mode(Pot::dot);
818 pc = r->comp_threshold_controllable();
822 pc = r->comp_speed_controllable();
826 pc = r->comp_mode_controllable();
827 _vpot->set_mode(Pot::wrap);
831 pc = r->comp_makeup_controllable();
840 if (r->mixbus () || r->is_master()) {
842 switch (global_pos) {
845 pc = r->pan_width_control();
849 pc = r->tape_drive_controllable();
855 eq_band = (global_pos-8);
856 pc = r->eq_gain_controllable (eq_band);
857 _vpot->set_mode(Pot::boost_cut);
861 } else if (r->is_input_strip ()) {
864 switch (global_pos) {
866 pc = r->filter_freq_controllable(true);
869 pc = r->filter_freq_controllable(false);
875 eq_band = (global_pos-8) / 2;
876 pc = r->eq_freq_controllable (eq_band);
882 eq_band = (global_pos-8) / 2;
883 pc = r->eq_gain_controllable (eq_band);
884 _vpot->set_mode(Pot::boost_cut);
888 #else //regular Mixbus channel EQ
890 switch (global_pos) {
892 pc = r->filter_freq_controllable(true);
897 eq_band = (global_pos-8) / 2;
898 pc = r->eq_gain_controllable (eq_band);
899 _vpot->set_mode(Pot::boost_cut);
904 eq_band = (global_pos-8) / 2;
905 pc = r->eq_freq_controllable (eq_band);
913 switch (global_pos) {
922 pc = r->send_level_controllable ( global_pos - 16 );
924 } //global_pos switch
927 #endif //ifdef MIXBUS
929 if (pc) { //control found; set our knob to watch for changes in it
930 _vpot->set_control (pc);
931 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_vpot_change, this), ui_context());
932 } else { //no control, just set the knob to "empty"
933 _vpot->reset_control ();
936 notify_vpot_change ();
940 Strip::set_vpot_parameter (AutomationType p)
942 if (!_stripable || (p == NullAutomation)) {
943 _vpot->set_control (boost::shared_ptr<AutomationControl>());
947 boost::shared_ptr<AutomationControl> pan_control;
949 DEBUG_TRACE (DEBUG::US2400, string_compose ("switch to vpot mode %1\n", p));
954 case PanAzimuthAutomation:
955 pan_control = _stripable->pan_azimuth_control ();
957 case PanWidthAutomation:
958 pan_control = _stripable->pan_width_control ();
960 case PanElevationAutomation:
962 case PanFrontBackAutomation:
964 case PanLFEAutomation:
972 _vpot->set_mode (Pot::dot);
973 _vpot->set_control (pan_control);
976 notify_panner_azi_changed (true);
980 Strip::is_midi_track () const
982 return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
988 _fader->mark_dirty();
996 Strip::notify_metering_state_changed()
998 if (_surface->mcp().subview_mode() != US2400Protocol::None) {
1002 if (!_stripable || !_meter) {
1006 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
1007 bool metering_active = _surface->mcp().metering_active ();
1009 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1013 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1015 if (!transport_is_rolling || !metering_active) {
1016 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1017 notify_panner_azi_changed (true);
1020 _transport_is_rolling = transport_is_rolling;
1021 _metering_active = metering_active;