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.
24 #include "pbd/memento_command.h"
25 #include "pbd/stacktrace.h"
27 #include "ardour/audioengine.h"
28 #include "ardour/automation_control.h"
29 #include "ardour/automation_watch.h"
30 #include "ardour/control_group.h"
31 #include "ardour/event_type_map.h"
32 #include "ardour/session.h"
33 #include "ardour/selection.h"
34 #include "ardour/value_as_string.h"
40 // C99 'isfinite()' is not available in MSVC.
41 #define isfinite_local(val) (bool)_finite((double)val)
43 #define isfinite_local isfinite
47 using namespace ARDOUR;
50 AutomationControl::AutomationControl(ARDOUR::Session& session,
51 const Evoral::Parameter& parameter,
52 const ParameterDescriptor& desc,
53 boost::shared_ptr<ARDOUR::AutomationList> list,
55 Controllable::Flag flags)
57 : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name, flags)
58 , Evoral::Control(parameter, desc, list)
63 set_flags (Controllable::Toggle);
65 boost::shared_ptr<AutomationList> al = alist();
67 al->StateChanged.connect_same_thread (_state_changed_connection, boost::bind (&Session::set_dirty, &_session));
71 AutomationControl::~AutomationControl ()
73 _session.selection().remove_control_by_id (id());
74 DropReferences (); /* EMIT SIGNAL */
78 AutomationControl::writable() const
80 boost::shared_ptr<AutomationList> al = alist();
82 return al->automation_state() != Play;
87 /** Get the current effective `user' value based on automation state */
89 AutomationControl::get_value() const
91 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
92 return Control::get_double (from_list, _session.transport_frame());
96 AutomationControl::pre_realtime_queue_stuff (double val, PBD::Controllable::GroupControlDisposition gcd)
98 if (_group && _group->use_me (gcd)) {
99 _group->pre_realtime_queue_stuff (val);
101 do_pre_realtime_queue_stuff (val);
106 AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
112 /* enforce strict double/boolean value mapping */
120 if (check_rt (val, gcd)) {
121 /* change has been queued to take place in an RT context */
125 if (_group && _group->use_me (gcd)) {
126 _group->set_group_value (shared_from_this(), val);
128 actually_set_value (val, gcd);
132 /** Set the value and do the right thing based on automation state
133 * (e.g. record if necessary, etc.)
134 * @param value `user' value
137 AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
139 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (_list);
140 const framepos_t pos = _session.transport_frame();
144 /* We cannot use ::get_value() here since that is virtual, and intended
145 to return a scalar value that in some way reflects the state of the
146 control (with semantics defined by the control itself, since it's
147 internal state may be more complex than can be fully represented by
150 This method's only job is to set the "user_double()" value of the
151 underlying Evoral::Control object, and so we should compare the new
152 value we're being given to the current user_double().
154 Unless ... we're doing automation playback, in which case the
155 current effective value of the control (used to determine if
156 anything has changed) is the one derived from the automation event
162 old_value = Control::user_double();
164 if (al->automation_write ()) {
166 old_value = Control::user_double ();
167 } else if (al->automation_playback()) {
169 old_value = al->eval (pos);
172 old_value = Control::user_double ();
176 Control::set_double (value, pos, to_list);
178 if (old_value != value) {
179 //AutomationType at = (AutomationType) _parameter.type();
180 //std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value
181 //<< " (was " << old_value << ") @ " << this << std::endl;
184 if (!al || !al->automation_playback ()) {
185 _session.set_dirty ();
192 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
194 Control::set_list (list);
195 Changed (true, Controllable::NoGroup);
199 AutomationControl::set_automation_state (AutoState as)
201 if (flags() & NotAutomatable) {
204 if (_list && as != alist()->automation_state()) {
206 const double val = get_value ();
208 alist()->set_automation_state (as);
210 Changed (false, Controllable::NoGroup); // notify slaves, update boolean masters
211 return; // No watch for boolean automation
215 AutomationWatch::instance().add_automation_watch (shared_from_this());
216 } else if (as == Touch) {
217 if (alist()->empty()) {
218 Control::set_double (val, _session.current_start_frame (), true);
219 Control::set_double (val, _session.current_end_frame (), true);
220 Changed (true, Controllable::NoGroup);
223 AutomationWatch::instance().remove_automation_watch (shared_from_this());
225 /* this seems unlikely, but the combination of
226 * a control surface and the mouse could make
227 * it possible to put the control into Touch
228 * mode *while* touching it.
230 AutomationWatch::instance().add_automation_watch (shared_from_this());
233 AutomationWatch::instance().remove_automation_watch (shared_from_this());
239 AutomationControl::set_automation_style (AutoStyle as)
242 alist()->set_automation_style (as);
246 AutomationControl::start_touch(double when)
254 if (alist()->automation_state() == Touch) {
255 /* subtle. aligns the user value with the playback */
256 set_value (get_value (), Controllable::NoGroup);
257 alist()->start_touch (when);
258 if (!_desc.toggled) {
259 AutomationWatch::instance().add_automation_watch (shared_from_this());
267 AutomationControl::stop_touch(bool mark, double when)
271 set_touching (false);
273 if (alist()->automation_state() == Touch) {
274 alist()->stop_touch (mark, when);
275 if (!_desc.toggled) {
276 AutomationWatch::instance().remove_automation_watch (shared_from_this());
284 AutomationControl::commit_transaction (bool did_write)
287 if (alist ()->before ()) {
288 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
289 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
292 alist ()->clear_history ();
297 AutomationControl::internal_to_interface (double val) const
299 if (_desc.integer_step) {
300 // both upper and lower are inclusive.
301 val = (val - lower()) / (1 + upper() - lower());
303 val = (val - lower()) / (upper() - lower());
306 if (_desc.logarithmic) {
308 val = pow (val, 1./2.0);
318 AutomationControl::interface_to_internal (double val) const
320 if (!isfinite_local (val)) {
323 if (_desc.logarithmic) {
327 val = pow (val, 2.0);
331 if (_desc.integer_step) {
332 val = lower() + val * (1 + upper() - lower());
334 val = lower() + val * (upper() - lower());
337 if (val < lower()) val = lower();
338 if (val > upper()) val = upper();
344 AutomationControl::get_user_string () const
346 return ARDOUR::value_as_string (_desc, get_value());
350 AutomationControl::set_group (boost::shared_ptr<ControlGroup> cg)
352 /* this method can only be called by a ControlGroup. We do not need
353 to ensure consistency by calling ControlGroup::remove_control(),
354 since we are guaranteed that the ControlGroup will take care of that
362 AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd)
364 if (!_session.loading() && (flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
365 /* queue change in RT context */
366 _session.set_control (shared_from_this(), val, gcd);