900e640e552c5668de9df0df03306ca66759cb3d
[ardour.git] / libs / ardour / slavable_automation_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 #ifndef __libardour_slavable_automation_control_h__
20 #define __libardour_slavable_automation_control_h__
21
22 #include "ardour/automation_control.h"
23 #include "ardour/session.h"
24
25 using namespace std;
26 using namespace ARDOUR;
27 using namespace PBD;
28
29 SlavableAutomationControl::SlavableAutomationControl(ARDOUR::Session& s,
30                                                      const Evoral::Parameter&                  parameter,
31                                                      const ParameterDescriptor&                desc,
32                                                      boost::shared_ptr<ARDOUR::AutomationList> l,
33                                                      const std::string&                        name)
34         : AutomationControl (s, parameter, desc, l, name)
35 {
36 }
37
38 SlavableAutomationControl::~SlavableAutomationControl ()
39 {
40
41 }
42
43 double
44 SlavableAutomationControl::get_masters_value_locked () const
45 {
46         gain_t v = 1.0;
47
48         for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
49                 /* get current master value, scale by our current ratio with that master */
50                 v *= mr->second.master()->get_value () * mr->second.ratio();
51         }
52
53         return min (_desc.upper, v);
54 }
55
56 double
57 SlavableAutomationControl::get_value_locked() const
58 {
59         /* read or write masters lock must be held */
60
61         if (_masters.empty()) {
62                 return Control::get_double (false, _session.transport_frame());
63         }
64
65         return get_masters_value_locked ();
66 }
67
68 /** Get the current effective `user' value based on automation state */
69 double
70 SlavableAutomationControl::get_value() const
71 {
72         bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
73
74         if (!from_list) {
75                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
76                 return get_value_locked ();
77         } else {
78                 return Control::get_double (from_list, _session.transport_frame());
79         }
80 }
81
82 void
83 SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
84 {
85         double current_value;
86         double new_value;
87         std::pair<Masters::iterator,bool> res;
88
89         {
90                 Glib::Threads::RWLock::WriterLock lm (master_lock);
91                 current_value = get_value_locked ();
92
93                 /* ratio will be recomputed below */
94
95                 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
96
97                 if (res.second) {
98
99                         recompute_masters_ratios (current_value);
100
101                         /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
102                            avoiding holding a reference to the control in the binding
103                            itself.
104                         */
105
106                         m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, m));
107
108                         /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
109                            and we no longer hear about changes to the AutomationControl.
110
111                            Note that we fix the "from_self" argument that will
112                            be given to our own Changed signal to "false",
113                            because the change came from the master.
114                         */
115
116
117                         m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2));
118                 }
119
120                 new_value = get_value_locked ();
121         }
122
123         if (res.second) {
124                 /* this will notify everyone that we're now slaved to the master */
125                 MasterStatusChange (); /* EMIT SIGNAL */
126         }
127
128         if (new_value != current_value) {
129                 /* force a call to to ::master_changed() to carry the
130                  * consequences that would occur if the master assumed
131                  * its current value WHILE we were slaved.
132                  */
133                 master_changed (false, Controllable::NoGroup);
134                 /* effective value changed by master */
135                 Changed (false, Controllable::NoGroup);
136         }
137
138 }
139
140 void
141 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
142 {
143         /* our value has (likely) changed, but not because we were
144          * modified. Just the master.
145          */
146
147         Changed (false, gcd); /* EMIT SIGNAL */
148 }
149
150 void
151 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
152 {
153         boost::shared_ptr<AutomationControl> m = wm.lock();
154         if (m) {
155                 remove_master (m);
156         }
157 }
158
159 void
160 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
161 {
162         double current_value;
163         double new_value;
164         Masters::size_type erased = 0;
165
166         {
167                 Glib::Threads::RWLock::WriterLock lm (master_lock);
168                 current_value = get_value_locked ();
169                 erased = _masters.erase (m->id());
170                 if (erased) {
171                         recompute_masters_ratios (current_value);
172                 }
173                 new_value = get_value_locked ();
174         }
175
176         if (erased) {
177                 MasterStatusChange (); /* EMIT SIGNAL */
178         }
179
180         if (new_value != current_value) {
181                 Changed (false, Controllable::NoGroup);
182         }
183 }
184
185 void
186 SlavableAutomationControl::clear_masters ()
187 {
188         double current_value;
189         double new_value;
190         bool had_masters = false;
191
192         {
193                 Glib::Threads::RWLock::WriterLock lm (master_lock);
194                 current_value = get_value_locked ();
195                 if (!_masters.empty()) {
196                         had_masters = true;
197                 }
198                 _masters.clear ();
199                 new_value = get_value_locked ();
200         }
201
202         if (had_masters) {
203                 MasterStatusChange (); /* EMIT SIGNAL */
204         }
205
206         if (new_value != current_value) {
207                 Changed (false, Controllable::NoGroup);
208         }
209
210 }
211
212 bool
213 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
214 {
215         Glib::Threads::RWLock::ReaderLock lm (master_lock);
216         return _masters.find (m->id()) != _masters.end();
217 }
218
219 bool
220 SlavableAutomationControl::slaved () const
221 {
222         Glib::Threads::RWLock::ReaderLock lm (master_lock);
223         return !_masters.empty();
224 }
225
226 #endif /* __libardour_slavable_automation_control_h__ */