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);
126 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
128 Control::set_list (list);
129 Changed (true, Controllable::NoGroup);
133 AutomationControl::set_automation_state (AutoState as)
135 if (_list && as != alist()->automation_state()) {
137 alist()->set_automation_state (as);
139 return; // No watch for boolean automation
143 AutomationWatch::instance().add_automation_watch (shared_from_this());
144 } else if (as == Touch) {
146 AutomationWatch::instance().remove_automation_watch (shared_from_this());
148 /* this seems unlikely, but the combination of
149 * a control surface and the mouse could make
150 * it possible to put the control into Touch
151 * mode *while* touching it.
153 AutomationWatch::instance().add_automation_watch (shared_from_this());
156 AutomationWatch::instance().remove_automation_watch (shared_from_this());
162 AutomationControl::set_automation_style (AutoStyle as)
165 alist()->set_automation_style (as);
169 AutomationControl::start_touch(double when)
177 if (alist()->automation_state() == Touch) {
178 /* subtle. aligns the user value with the playback */
179 set_value (get_value (), Controllable::NoGroup);
180 alist()->start_touch (when);
181 if (!_desc.toggled) {
182 AutomationWatch::instance().add_automation_watch (shared_from_this());
190 AutomationControl::stop_touch(bool mark, double when)
194 set_touching (false);
196 if (alist()->automation_state() == Touch) {
197 alist()->stop_touch (mark, when);
198 if (!_desc.toggled) {
199 AutomationWatch::instance().remove_automation_watch (shared_from_this());
207 AutomationControl::commit_transaction (bool did_write)
210 if (alist ()->before ()) {
211 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
212 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
215 alist ()->clear_history ();
220 AutomationControl::internal_to_interface (double val) const
222 if (_desc.integer_step) {
223 // both upper and lower are inclusive.
224 val = (val - lower()) / (1 + upper() - lower());
226 val = (val - lower()) / (upper() - lower());
229 if (_desc.logarithmic) {
231 val = pow (val, 1./2.0);
241 AutomationControl::interface_to_internal (double val) const
243 if (!isfinite_local (val)) {
246 if (_desc.logarithmic) {
250 val = pow (val, 2.0);
254 if (_desc.integer_step) {
255 val = lower() + val * (1 + upper() - lower());
257 val = lower() + val * (upper() - lower());
260 if (val < lower()) val = lower();
261 if (val > upper()) val = upper();
268 AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
270 double current_value;
272 std::pair<Masters::iterator,bool> res;
275 Glib::Threads::RWLock::WriterLock lm (master_lock);
276 current_value = get_value_locked ();
278 /* ratio will be recomputed below */
280 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
284 recompute_masters_ratios (current_value);
286 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
287 avoiding holding a reference to the control in the binding
291 m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
293 /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
294 and we no longer hear about changes to the AutomationControl.
296 Note that we fix the "from_self" argument that will
297 be given to our own Changed signal to "false",
298 because the change came from the master.
302 m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&AutomationControl::master_changed, this, _1, _2));
305 new_value = get_value_locked ();
309 /* this will notify everyone that we're now slaved to the master */
310 MasterStatusChange (); /* EMIT SIGNAL */
313 if (new_value != current_value) {
314 /* force a call to to ::master_changed() to carry the
315 * consequences that would occur if the master assumed
316 * its current value WHILE we were slaved.
318 master_changed (false, Controllable::NoGroup);
319 /* effective value changed by master */
320 Changed (false, Controllable::NoGroup);
326 AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
328 /* our value has (likely) changed, but not because we were
329 * modified. Just the master.
332 Changed (false, gcd); /* EMIT SIGNAL */
336 AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
338 boost::shared_ptr<AutomationControl> m = wm.lock();
345 AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
347 double current_value;
349 Masters::size_type erased = 0;
352 Glib::Threads::RWLock::WriterLock lm (master_lock);
353 current_value = get_value_locked ();
354 erased = _masters.erase (m->id());
356 recompute_masters_ratios (current_value);
358 new_value = get_value_locked ();
362 MasterStatusChange (); /* EMIT SIGNAL */
365 if (new_value != current_value) {
366 Changed (false, Controllable::NoGroup);
371 AutomationControl::clear_masters ()
373 double current_value;
375 bool had_masters = false;
378 Glib::Threads::RWLock::WriterLock lm (master_lock);
379 current_value = get_value_locked ();
380 if (!_masters.empty()) {
384 new_value = get_value_locked ();
388 MasterStatusChange (); /* EMIT SIGNAL */
391 if (new_value != current_value) {
392 Changed (false, Controllable::NoGroup);
398 AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
400 Glib::Threads::RWLock::ReaderLock lm (master_lock);
401 return _masters.find (m->id()) != _masters.end();
405 AutomationControl::slaved () const
407 Glib::Threads::RWLock::ReaderLock lm (master_lock);
408 return !_masters.empty();