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 ();
266 // if (!_stripable) {
270 // The active V-pot control may not be active for this strip
271 // But if we zero it in the controls function it may erase
272 // the one we do want
273 // _surface->write (_vpot->zero());
275 notify_solo_changed ();
276 notify_mute_changed ();
277 notify_gain_changed ();
278 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
279 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::selected));
280 notify_panner_azi_changed ();
281 notify_vpot_change ();
282 notify_panner_width_changed ();
283 notify_record_enable_changed ();
284 // notify_processor_changed ();
288 Strip::notify_solo_changed ()
290 // if (_stripable && _solo) {
291 // _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
294 _solo->mark_dirty ();
295 _trickle_counter = 0;
299 Strip::notify_mute_changed ()
301 DEBUG_TRACE (DEBUG::US2400, string_compose ("Strip %1 mute changed\n", _index));
302 // if (_stripable && _mute) {
303 // DEBUG_TRACE (DEBUG::US2400, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
304 // DEBUG_TRACE (DEBUG::US2400, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
306 // _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
308 // _surface->write (_mute->zero());
311 _mute->mark_dirty ();
312 _trickle_counter = 0;
316 Strip::notify_record_enable_changed ()
321 Strip::notify_stripable_deleted ()
323 _surface->mcp().notify_stripable_removed ();
324 _surface->mcp().refresh_current_bank();
328 Strip::notify_gain_changed (bool force_update)
330 _fader->mark_dirty();
331 _trickle_counter = 0;
335 Strip::notify_processor_changed (bool force_update)
340 Strip::notify_property_changed (const PropertyChange& what_changed)
345 Strip::update_selection_state ()
347 _select->mark_dirty ();
348 _trickle_counter = 0;
351 // _surface->write (_select->set_state (_stripable->is_selected()));
356 Strip::show_stripable_name ()
361 Strip::notify_vpot_change ()
364 _trickle_counter = 0;
368 Strip::notify_panner_azi_changed (bool force_update)
371 _trickle_counter = 0;
375 Strip::notify_panner_width_changed (bool force_update)
377 _trickle_counter = 0;
381 Strip::select_event (Button&, ButtonState bs)
383 DEBUG_TRACE (DEBUG::US2400, "select button\n");
387 int ms = _surface->mcp().main_modifier_state();
389 if (ms & US2400Protocol::MODIFIER_CMDALT) {
390 _controls_locked = !_controls_locked;
394 DEBUG_TRACE (DEBUG::US2400, "add select button on press\n");
395 _surface->mcp().add_down_select_button (_surface->number(), _index);
396 _surface->mcp().select_range (_surface->mcp().global_index (*this));
399 DEBUG_TRACE (DEBUG::US2400, "remove select button on release\n");
400 _surface->mcp().remove_down_select_button (_surface->number(), _index);
403 _trickle_counter = 0;
407 Strip::vselect_event (Button&, ButtonState bs)
412 Strip::fader_touch_event (Button&, ButtonState bs)
414 DEBUG_TRACE (DEBUG::US2400, string_compose ("fader touch, press ? %1\n", (bs == press)));
418 boost::shared_ptr<AutomationControl> ac = _fader->control ();
420 _fader->set_in_use (true);
421 _fader->start_touch (_surface->mcp().transport_frame());
425 _fader->set_in_use (false);
426 _fader->stop_touch (_surface->mcp().transport_frame());
433 Strip::handle_button (Button& button, ButtonState bs)
435 boost::shared_ptr<AutomationControl> control;
438 button.set_in_use (true);
440 button.set_in_use (false);
443 DEBUG_TRACE (DEBUG::US2400, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
445 switch (button.bid()) {
447 select_event (button, bs);
450 case Button::FaderTouch:
451 fader_touch_event (button, bs);
455 if ((control = button.control ())) {
457 DEBUG_TRACE (DEBUG::US2400, "add button on press\n");
458 _surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
460 float new_value = control->get_value() ? 0.0 : 1.0;
462 /* get all controls that either have their
463 * button down or are within a range of
464 * several down buttons
467 US2400Protocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
468 _surface->mcp().global_index(*this));
471 DEBUG_TRACE (DEBUG::US2400, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
472 controls.size(), control->parameter().type(), new_value));
474 /* apply change, with potential modifier semantics */
476 Controllable::GroupControlDisposition gcd;
478 if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
479 gcd = Controllable::InverseGroup;
481 gcd = Controllable::UseGroup;
484 for (US2400Protocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
485 (*c)->set_value (new_value, gcd);
489 DEBUG_TRACE (DEBUG::US2400, "remove button on release\n");
490 _surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
499 Strip::handle_fader_touch (Fader& fader, bool touch_on)
502 fader.start_touch (_surface->mcp().transport_frame());
504 fader.stop_touch (_surface->mcp().transport_frame());
509 Strip::handle_fader (Fader& fader, float position)
511 DEBUG_TRACE (DEBUG::US2400, string_compose ("fader to %1\n", position));
512 boost::shared_ptr<AutomationControl> ac = fader.control();
517 Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
519 if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
520 gcd = Controllable::InverseGroup;
523 fader.set_value (position, gcd);
525 /* From the Mackie Control MIDI implementation docs:
527 In order to ensure absolute synchronization with the host software,
528 Mackie Control uses a closed-loop servo system for the faders,
529 meaning the faders will always move to their last received position.
530 When a host receives a Fader Position Message, it must then
531 re-transmit that message to the Mackie Control or else the faders
532 will return to their last position.
535 _surface->write (fader.set_position (position));
539 Strip::handle_pot (Pot& pot, float delta)
541 /* Pots only emit events when they move, not when they
542 stop moving. So to get a stop event, we need to use a timeout.
545 boost::shared_ptr<AutomationControl> ac = pot.control();
550 Controllable::GroupControlDisposition gcd;
552 if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
553 gcd = Controllable::InverseGroup;
555 gcd = Controllable::UseGroup;
560 /* make it like a single-step, directional switch */
563 ac->set_value (1.0, gcd);
565 ac->set_value (0.0, gcd);
568 } else if (ac->desc().enumeration || ac->desc().integer_step) {
570 /* use Controllable::get_value() to avoid the
571 * "scaling-to-interface" that takes place in
572 * Control::get_value() via the pot member.
574 * an enumeration with 4 values will have interface values of
575 * 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
580 ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
582 ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
587 double p = ac->get_interface();
594 ac->set_value ( ac->interface_to_internal(p), gcd);
599 Strip::periodic (ARDOUR::microseconds_t now)
604 if ( _trickle_counter %24 == 0 ) {
606 if ( _fader->control() ) {
607 _surface->write (_fader->set_position (_fader->control()->internal_to_interface (_fader->control()->get_value ())));
609 _surface->write (_fader->set_position(0.0));
612 if ( _vpot->control() ) {
613 _surface->write (_vpot->set (_vpot->control()->internal_to_interface (_vpot->control()->get_value ()), true));
615 _surface->write (_vpot->set(0.0, false));
619 _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
620 _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
621 _surface->write (_select->set_state (_stripable->is_selected()));
623 _surface->write (_solo->set_state (off));
624 _surface->write (_mute->set_state (off));
625 _surface->write (_select->set_state (off));
630 //after a hard write, queue us for trickling data later
631 if (_trickle_counter == 0)
632 _trickle_counter = global_index()+1;
639 Strip::redisplay (ARDOUR::microseconds_t now, bool force)
644 Strip::update_automation ()
649 Strip::update_meter ()
655 if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
656 float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
657 _meter->send_update (*_surface, dB);
665 _trickle_counter = 0;
669 Strip::lock_controls ()
671 _controls_locked = true;
675 Strip::unlock_controls ()
677 _controls_locked = false;
681 Strip::vpot_mode_string ()
687 Strip::next_pot_mode ()
689 vector<AutomationType>::iterator i;
691 boost::shared_ptr<AutomationControl> ac = _vpot->control();
698 if (_surface->mcp().subview_mode() != US2400Protocol::None) {
702 if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
706 for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
707 if ((*i) == ac->parameter().type()) {
712 /* move to the next mode in the list, or back to the start (which will
713 also happen if the current mode is not in the current pot mode list)
716 if (i != possible_pot_parameters.end()) {
720 if (i == possible_pot_parameters.end()) {
721 i = possible_pot_parameters.begin();
724 set_vpot_parameter (*i);
730 * name: Strip::subview_mode_changed
735 Strip::subview_mode_changed ()
737 switch (_surface->mcp().subview_mode()) {
739 case US2400Protocol::None:
740 set_vpot_parameter (_pan_mode);
741 notify_metering_state_changed ();
744 case US2400Protocol::TrackView:
745 boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
747 DEBUG_TRACE (DEBUG::US2400, string_compose("subview_mode_changed strip %1:%2- assigning trackview pot\n", _surface->number(), _index));
748 setup_trackview_vpot (r);
750 DEBUG_TRACE (DEBUG::US2400, string_compose("subview_mode_changed strip %1:%2 - no stripable\n", _surface->number(), _index));
756 _trickle_counter = 0;
760 Strip::setup_dyn_vpot (boost::shared_ptr<Stripable> r)
765 Strip::setup_eq_vpot (boost::shared_ptr<Stripable> r)
770 Strip::setup_sends_vpot (boost::shared_ptr<Stripable> r)
776 Strip::setup_trackview_vpot (boost::shared_ptr<Stripable> r)
778 subview_connections.drop_connections ();
785 boost::shared_ptr<AutomationControl> pc;
786 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
789 _vpot->set_mode(Pot::wrap);
792 const uint32_t global_pos = _surface->mcp().global_index (*this);
795 switch (global_pos) {
797 pc = r->trim_control ();
798 _vpot->set_mode(Pot::boost_cut);
802 pc = r->pan_azimuth_control ();
803 _vpot->set_mode(Pot::dot);
807 pc = r->comp_threshold_controllable();
811 pc = r->comp_speed_controllable();
815 pc = r->comp_mode_controllable();
816 _vpot->set_mode(Pot::wrap);
820 pc = r->comp_makeup_controllable();
829 if (r->mixbus () || r->is_master()) {
831 switch (global_pos) {
834 pc = r->pan_width_control();
838 pc = r->tape_drive_controllable();
844 eq_band = (global_pos-8);
845 pc = r->eq_gain_controllable (eq_band);
846 _vpot->set_mode(Pot::boost_cut);
850 } else if (r->is_input_strip ()) {
853 switch (global_pos) {
855 pc = r->filter_freq_controllable(true);
858 pc = r->filter_freq_controllable(false);
864 eq_band = (global_pos-8) / 2;
865 pc = r->eq_freq_controllable (eq_band);
871 eq_band = (global_pos-8) / 2;
872 pc = r->eq_gain_controllable (eq_band);
873 _vpot->set_mode(Pot::boost_cut);
877 #else //regular Mixbus channel EQ
879 switch (global_pos) {
881 pc = r->filter_freq_controllable(true);
886 eq_band = (global_pos-8) / 2;
887 pc = r->eq_gain_controllable (eq_band);
888 _vpot->set_mode(Pot::boost_cut);
893 eq_band = (global_pos-8) / 2;
894 pc = r->eq_freq_controllable (eq_band);
902 switch (global_pos) {
911 pc = r->send_level_controllable ( global_pos - 16 );
913 } //global_pos switch
916 #endif //ifdef MIXBUS
918 if (pc) { //control found; set our knob to watch for changes in it
919 _vpot->set_control (pc);
920 pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_vpot_change, this), ui_context());
921 } else { //no control, just set the knob to "empty"
922 _vpot->reset_control ();
925 notify_vpot_change ();
929 Strip::set_vpot_parameter (AutomationType p)
931 if (!_stripable || (p == NullAutomation)) {
932 _vpot->set_control (boost::shared_ptr<AutomationControl>());
936 boost::shared_ptr<AutomationControl> pan_control;
938 DEBUG_TRACE (DEBUG::US2400, string_compose ("switch to vpot mode %1\n", p));
943 case PanAzimuthAutomation:
944 pan_control = _stripable->pan_azimuth_control ();
946 case PanWidthAutomation:
947 pan_control = _stripable->pan_width_control ();
949 case PanElevationAutomation:
951 case PanFrontBackAutomation:
953 case PanLFEAutomation:
961 _vpot->set_mode (Pot::dot);
962 _vpot->set_control (pan_control);
965 notify_panner_azi_changed (true);
969 Strip::is_midi_track () const
971 return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
977 _fader->mark_dirty();
985 Strip::notify_metering_state_changed()
987 if (_surface->mcp().subview_mode() != US2400Protocol::None) {
991 if (!_stripable || !_meter) {
995 bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
996 bool metering_active = _surface->mcp().metering_active ();
998 if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
1002 _meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
1004 if (!transport_is_rolling || !metering_active) {
1005 notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
1006 notify_panner_azi_changed (true);
1009 _transport_is_rolling = transport_is_rolling;
1010 _metering_active = metering_active;