45ec5b90ec8bace7c72308e71aa0c4b02351e938
[ardour.git] / libs / ardour / solo_control.cc
1 /*
2     Copyright (C) 2016 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify it
5     under the terms of the GNU General Public License as published by the Free
6     Software Foundation; either version 2 of the License, or (at your option)
7     any later version.
8
9     This program is distributed in the hope that it will be useful, but WITHOUT
10     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12     for more details.
13
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "ardour/debug.h"
20 #include "ardour/mute_master.h"
21 #include "ardour/session.h"
22 #include "ardour/solo_control.h"
23
24 #include "i18n.h"
25
26 using namespace ARDOUR;
27 using namespace std;
28 using namespace PBD;
29
30 SoloControl::SoloControl (Session& session, std::string const & name, Soloable& s, Muteable& m)
31         : SlavableAutomationControl (session, SoloAutomation, ParameterDescriptor (SoloAutomation),
32                                      boost::shared_ptr<AutomationList>(new AutomationList(Evoral::Parameter(SoloAutomation))),
33                                      name)
34         , _soloable (s)
35         , _muteable (m)
36         , _self_solo (false)
37         , _soloed_by_others_upstream (0)
38         , _soloed_by_others_downstream (0)
39 {
40         _list->set_interpolation (Evoral::ControlList::Discrete);
41         /* solo changes must be synchronized by the process cycle */
42         set_flags (Controllable::Flag (flags() | Controllable::RealTime));
43 }
44
45 void
46 SoloControl::set_self_solo (bool yn)
47 {
48         DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
49         _self_solo = yn;
50         set_mute_master_solo ();
51 }
52
53 void
54 SoloControl::set_mute_master_solo ()
55 {
56         _muteable.mute_master()->set_soloed_by_self (self_soloed());
57
58         if (Config->get_solo_control_is_listen_control()) {
59                 _muteable.mute_master()->set_soloed_by_others (false);
60         } else {
61                 _muteable.mute_master()->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
62         }
63 }
64
65 void
66 SoloControl::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
67 {
68         if (_soloable.is_safe() || !_soloable.can_solo()) {
69                 return;
70         }
71
72         bool master_soloed;
73
74         {
75                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
76                 master_soloed = (bool) get_masters_value_locked ();
77         }
78
79         /* Master is considered equivalent to an upstream solo control, not
80          * direct control over self-soloed.
81          */
82
83         mod_solo_by_others_upstream (master_soloed ? 1 : -1);
84
85         /* no need to call AutomationControl::master_changed() since it just
86            emits Changed() which we already did in mod_solo_by_others_upstream()
87         */
88 }
89
90 void
91 SoloControl::mod_solo_by_others_downstream (int32_t delta)
92 {
93         if (_soloable.is_safe() || !_soloable.can_solo()) {
94                 return;
95         }
96
97         DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
98                                                   name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
99
100         if (delta < 0) {
101                 if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
102                         _soloed_by_others_downstream += delta;
103                 } else {
104                         _soloed_by_others_downstream = 0;
105                 }
106         } else {
107                 _soloed_by_others_downstream += delta;
108         }
109
110         DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
111
112         set_mute_master_solo ();
113         Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
114 }
115
116 void
117 SoloControl::mod_solo_by_others_upstream (int32_t delta)
118 {
119         if (_soloable.is_safe() || !_soloable.can_solo()) {
120                 return;
121         }
122
123         DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
124                                                   name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
125
126         uint32_t old_sbu = _soloed_by_others_upstream;
127
128         if (delta < 0) {
129                 if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
130                         _soloed_by_others_upstream += delta;
131                 } else {
132                         _soloed_by_others_upstream = 0;
133                 }
134         } else {
135                 _soloed_by_others_upstream += delta;
136         }
137
138         DEBUG_TRACE (DEBUG::Solo, string_compose (
139                              "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
140                              name(), delta, _soloed_by_others_upstream, old_sbu,
141                              _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
142
143
144         /* push the inverse solo change to everything that feeds us.
145
146            This is important for solo-within-group. When we solo 1 track out of N that
147            feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
148            on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
149            tracks that feed it. This will silence them if they were audible because
150            of a bus solo, but the newly soloed track will still be audible (because
151            it is self-soloed).
152
153            but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
154                    not in reverse.
155         */
156
157         if ((_self_solo || _soloed_by_others_downstream) &&
158             ((old_sbu == 0 && _soloed_by_others_upstream > 0) ||
159              (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
160
161                 if (delta > 0 || !Config->get_exclusive_solo()) {
162                         _soloable.push_solo_upstream (delta);
163                 }
164         }
165
166         set_mute_master_solo ();
167         Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
168 }
169
170 void
171 SoloControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
172 {
173         if (_soloable.is_safe() || !_soloable.can_solo()) {
174                 return;
175         }
176
177         set_self_solo (val == 1.0);
178
179         /* this sets the Evoral::Control::_user_value for us, which will
180            be retrieved by AutomationControl::get_value (), and emits Changed
181         */
182
183         AutomationControl::actually_set_value (val, group_override);
184         _session.set_dirty ();
185 }
186
187 double
188 SoloControl::get_value () const
189 {
190         if (slaved()) {
191                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
192                 return get_masters_value_locked () ? 1.0 : 0.0;
193         }
194
195         std::cerr << "solo control @ " << this << " list = " << _list << " as AL " << boost::dynamic_pointer_cast<AutomationList>(_list) << std::endl;
196
197         if (_list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback()) {
198                 // Playing back automation, get the value from the list
199                 return AutomationControl::get_value();
200         }
201
202         return self_soloed() ? 1.0 : 0.0;
203 }
204
205 void
206 SoloControl::clear_all_solo_state ()
207 {
208         // ideally this function will never do anything, it only exists to forestall Murphy
209
210 #ifndef NDEBUG
211         // these are really debug messages, but of possible interest.
212         if (self_soloed()) {
213                 PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
214         }
215         if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
216                 PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
217                                 name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
218         }
219 #endif
220
221         _soloed_by_others_upstream = 0;
222         _soloed_by_others_downstream = 0;
223
224         set_self_solo (false);
225
226         Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
227 }
228
229 int
230 SoloControl::set_state (XMLNode const & node, int)
231 {
232         XMLProperty const * prop;
233
234         if ((prop = node.property ("self-solo")) != 0) {
235                 set_self_solo (string_is_affirmative (prop->value()));
236         }
237
238         if ((prop = node.property ("soloed-by-upstream")) != 0) {
239                 _soloed_by_others_upstream = 0; // needed for mod_.... () to work
240                 mod_solo_by_others_upstream (atoi (prop->value()));
241         }
242
243         if ((prop = node.property ("soloed-by-downstream")) != 0) {
244                 _soloed_by_others_downstream = 0; // needed for mod_.... () to work
245                 mod_solo_by_others_downstream (atoi (prop->value()));
246         }
247
248         return 0;
249 }
250
251 XMLNode&
252 SoloControl::get_state ()
253 {
254         XMLNode& node (SlavableAutomationControl::get_state());
255
256         node.add_property (X_("self-solo"), _self_solo ? X_("yes") : X_("no"));
257         char buf[32];
258         snprintf (buf, sizeof(buf), "%d",  _soloed_by_others_upstream);
259         node.add_property (X_("soloed-by-upstream"), buf);
260         snprintf (buf, sizeof(buf), "%d",  _soloed_by_others_downstream);
261         node.add_property (X_("soloed-by-downstream"), buf);
262
263         return node;
264 }