2 Copyright (C) 2007 Paul Davis
3 Author: David Robillard
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.
23 #include "ardour/automation_control.h"
24 #include "ardour/automation_watch.h"
25 #include "ardour/event_type_map.h"
26 #include "ardour/session.h"
28 #include "pbd/memento_command.h"
29 #include "pbd/stacktrace.h"
35 // C99 'isfinite()' is not available in MSVC.
36 #define isfinite_local(val) (bool)_finite((double)val)
38 #define isfinite_local isfinite
42 using namespace ARDOUR;
45 AutomationControl::AutomationControl(ARDOUR::Session& session,
46 const Evoral::Parameter& parameter,
47 const ParameterDescriptor& desc,
48 boost::shared_ptr<ARDOUR::AutomationList> list,
50 : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name)
51 , Evoral::Control(parameter, desc, list)
57 AutomationControl::~AutomationControl ()
59 DropReferences (); /* EMIT SIGNAL */
63 AutomationControl::writable() const
65 boost::shared_ptr<AutomationList> al = alist();
67 return al->automation_state() != Play;
73 AutomationControl::get_masters_value_locked () const
77 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
78 /* get current master value, scale by our current ratio with that master */
79 v *= mr->second.master()->get_value () * mr->second.ratio();
82 return min (_desc.upper, v);
86 AutomationControl::get_value_locked() const
88 /* read or write masters lock must be held */
90 if (_masters.empty()) {
91 return Control::get_double (false, _session.transport_frame());
94 return get_masters_value_locked ();
97 /** Get the current effective `user' value based on automation state */
99 AutomationControl::get_value() const
101 bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
104 Glib::Threads::RWLock::ReaderLock lm (master_lock);
105 return get_value_locked ();
107 return Control::get_double (from_list, _session.transport_frame());
111 /** Set the value and do the right thing based on automation state
112 * (e.g. record if necessary, etc.)
113 * @param value `user' value
116 AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
118 bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
120 Control::set_double (value, _session.transport_frame(), to_list);
122 cerr << "AC was set to " << value << endl;
128 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
130 Control::set_list (list);
131 Changed (true, Controllable::NoGroup);
135 AutomationControl::set_automation_state (AutoState as)
137 if (_list && as != alist()->automation_state()) {
139 alist()->set_automation_state (as);
141 return; // No watch for boolean automation
145 AutomationWatch::instance().add_automation_watch (shared_from_this());
146 } else if (as == Touch) {
148 AutomationWatch::instance().remove_automation_watch (shared_from_this());
150 /* this seems unlikely, but the combination of
151 * a control surface and the mouse could make
152 * it possible to put the control into Touch
153 * mode *while* touching it.
155 AutomationWatch::instance().add_automation_watch (shared_from_this());
158 AutomationWatch::instance().remove_automation_watch (shared_from_this());
164 AutomationControl::set_automation_style (AutoStyle as)
167 alist()->set_automation_style (as);
171 AutomationControl::start_touch(double when)
179 if (alist()->automation_state() == Touch) {
180 /* subtle. aligns the user value with the playback */
181 set_value (get_value (), Controllable::NoGroup);
182 alist()->start_touch (when);
183 if (!_desc.toggled) {
184 AutomationWatch::instance().add_automation_watch (shared_from_this());
192 AutomationControl::stop_touch(bool mark, double when)
196 set_touching (false);
198 if (alist()->automation_state() == Touch) {
199 alist()->stop_touch (mark, when);
200 if (!_desc.toggled) {
201 AutomationWatch::instance().remove_automation_watch (shared_from_this());
209 AutomationControl::commit_transaction (bool did_write)
212 if (alist ()->before ()) {
213 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
214 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
217 alist ()->clear_history ();
222 AutomationControl::internal_to_interface (double val) const
224 if (_desc.integer_step) {
225 // both upper and lower are inclusive.
226 val = (val - lower()) / (1 + upper() - lower());
228 val = (val - lower()) / (upper() - lower());
231 if (_desc.logarithmic) {
233 val = pow (val, 1./2.0);
243 AutomationControl::interface_to_internal (double val) const
245 if (!isfinite_local (val)) {
248 if (_desc.logarithmic) {
252 val = pow (val, 2.0);
256 if (_desc.integer_step) {
257 val = lower() + val * (1 + upper() - lower());
259 val = lower() + val * (upper() - lower());
262 if (val < lower()) val = lower();
263 if (val > upper()) val = upper();
270 AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
272 double current_value;
274 std::pair<Masters::iterator,bool> res;
277 Glib::Threads::RWLock::WriterLock lm (master_lock);
278 current_value = get_value_locked ();
280 /* ratio will be recomputed below */
282 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
286 recompute_masters_ratios (current_value);
288 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
289 avoiding holding a reference to the control in the binding
293 m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
295 /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
296 and we no longer hear about changes to the AutomationControl.
298 Note that we fix the "from_self" argument that will
299 be given to our own Changed signal to "false",
300 because the change came from the master.
304 m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal2<void,bool,Controllable::GroupControlDisposition>::operator(), &Changed, false, _2));
307 new_value = get_value_locked ();
311 MasterStatusChange (); /* EMIT SIGNAL */
314 if (new_value != current_value) {
315 /* effective value changed by master */
316 Changed (false, Controllable::NoGroup);
322 AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
324 boost::shared_ptr<AutomationControl> m = wm.lock();
331 AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
333 double current_value;
335 Masters::size_type erased = 0;
338 Glib::Threads::RWLock::WriterLock lm (master_lock);
339 current_value = get_value_locked ();
340 erased = _masters.erase (m->id());
342 recompute_masters_ratios (current_value);
344 new_value = get_value_locked ();
348 MasterStatusChange (); /* EMIT SIGNAL */
351 if (new_value != current_value) {
352 Changed (false, Controllable::NoGroup);
357 AutomationControl::clear_masters ()
359 double current_value;
361 bool had_masters = false;
364 Glib::Threads::RWLock::WriterLock lm (master_lock);
365 current_value = get_value_locked ();
366 if (!_masters.empty()) {
370 new_value = get_value_locked ();
374 MasterStatusChange (); /* EMIT SIGNAL */
377 if (new_value != current_value) {
378 Changed (false, Controllable::NoGroup);
384 AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
386 Glib::Threads::RWLock::ReaderLock lm (master_lock);
387 return _masters.find (m->id()) != _masters.end();
391 AutomationControl::slaved () const
393 Glib::Threads::RWLock::ReaderLock lm (master_lock);
394 return !_masters.empty();