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;
72 /** Get the current effective `user' value based on automation state */
74 AutomationControl::get_value() const
76 bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
79 Glib::Threads::RWLock::ReaderLock lm (master_lock);
80 return get_value_locked ();
82 return Control::get_double (from_list, _session.transport_frame());
87 AutomationControl::get_value_locked() const
89 /* read or write masters lock must be held */
91 if (_masters.empty()) {
92 return Control::get_double (false, _session.transport_frame());
97 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
98 /* get current master value, scale by our current ratio with that master */
99 v *= mr->second.master()->get_value () * mr->second.ratio();
102 return min (_desc.upper, v);
107 /** Set the value and do the right thing based on automation state
108 * (e.g. record if necessary, etc.)
109 * @param value `user' value
112 AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition /* group_override */)
114 bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
116 Control::set_double (value, _session.transport_frame(), to_list);
118 Changed(); /* EMIT SIGNAL */
122 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
124 Control::set_list (list);
125 Changed(); /* EMIT SIGNAL */
129 AutomationControl::set_automation_state (AutoState as)
131 if (_list && as != alist()->automation_state()) {
133 alist()->set_automation_state (as);
135 return; // No watch for boolean automation
139 AutomationWatch::instance().add_automation_watch (shared_from_this());
140 } else if (as == Touch) {
142 AutomationWatch::instance().remove_automation_watch (shared_from_this());
144 /* this seems unlikely, but the combination of
145 * a control surface and the mouse could make
146 * it possible to put the control into Touch
147 * mode *while* touching it.
149 AutomationWatch::instance().add_automation_watch (shared_from_this());
152 AutomationWatch::instance().remove_automation_watch (shared_from_this());
158 AutomationControl::set_automation_style (AutoStyle as)
161 alist()->set_automation_style (as);
165 AutomationControl::start_touch(double when)
173 if (alist()->automation_state() == Touch) {
174 /* subtle. aligns the user value with the playback */
175 set_value (get_value (), Controllable::NoGroup);
176 alist()->start_touch (when);
177 if (!_desc.toggled) {
178 AutomationWatch::instance().add_automation_watch (shared_from_this());
186 AutomationControl::stop_touch(bool mark, double when)
190 set_touching (false);
192 if (alist()->automation_state() == Touch) {
193 alist()->stop_touch (mark, when);
194 if (!_desc.toggled) {
195 AutomationWatch::instance().remove_automation_watch (shared_from_this());
203 AutomationControl::commit_transaction (bool did_write)
206 if (alist ()->before ()) {
207 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
208 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
211 alist ()->clear_history ();
216 AutomationControl::internal_to_interface (double val) const
218 if (_desc.integer_step) {
219 // both upper and lower are inclusive.
220 val = (val - lower()) / (1 + upper() - lower());
222 val = (val - lower()) / (upper() - lower());
225 if (_desc.logarithmic) {
227 val = pow (val, 1./2.0);
237 AutomationControl::interface_to_internal (double val) const
239 if (!isfinite_local (val)) {
242 if (_desc.logarithmic) {
246 val = pow (val, 2.0);
250 if (_desc.integer_step) {
251 val = lower() + val * (1 + upper() - lower());
253 val = lower() + val * (upper() - lower());
256 if (val < lower()) val = lower();
257 if (val > upper()) val = upper();
264 AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
266 double current_value;
267 std::pair<Masters::iterator,bool> res;
270 Glib::Threads::RWLock::WriterLock lm (master_lock);
271 current_value = get_value_locked ();
273 /* ratio will be recomputed below */
275 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
279 recompute_masters_ratios (current_value);
281 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
282 avoiding holding a reference to the control in the binding
286 m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
288 /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
289 and we no longer hear about changes to the AutomationControl.
292 m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
297 MasterStatusChange (); /* EMIT SIGNAL */
302 AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
304 boost::shared_ptr<AutomationControl> m = wm.lock();
311 AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
313 double current_value;
314 Masters::size_type erased = 0;
317 Glib::Threads::RWLock::WriterLock lm (master_lock);
318 current_value = get_value_locked ();
319 erased = _masters.erase (m->id());
321 recompute_masters_ratios (current_value);
326 MasterStatusChange (); /* EMIT SIGNAL */
331 AutomationControl::clear_masters ()
333 bool had_masters = false;
336 Glib::Threads::RWLock::WriterLock lm (master_lock);
337 if (!_masters.empty()) {
344 MasterStatusChange (); /* EMIT SIGNAL */
349 AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
351 Glib::Threads::RWLock::ReaderLock lm (master_lock);
352 return _masters.find (m->id()) != _masters.end();
356 AutomationControl::slaved () const
358 Glib::Threads::RWLock::ReaderLock lm (master_lock);
359 return !_masters.empty();