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"
38 // C99 'isfinite()' is not available in MSVC.
39 #define isfinite_local(val) (bool)_finite((double)val)
41 #define isfinite_local isfinite
45 using namespace ARDOUR;
48 AutomationControl::AutomationControl(ARDOUR::Session& session,
49 const Evoral::Parameter& parameter,
50 const ParameterDescriptor& desc,
51 boost::shared_ptr<ARDOUR::AutomationList> list,
53 Controllable::Flag flags)
55 : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name, flags)
56 , Evoral::Control(parameter, desc, list)
61 set_flags (Controllable::Toggle);
63 boost::shared_ptr<AutomationList> al = alist();
65 al->StateChanged.connect_same_thread (_state_changed_connection, boost::bind (&Session::set_dirty, &_session));
69 AutomationControl::~AutomationControl ()
71 DropReferences (); /* EMIT SIGNAL */
75 AutomationControl::writable() const
77 boost::shared_ptr<AutomationList> al = alist();
79 return al->automation_state() != Play;
84 /** Get the current effective `user' value based on automation state */
86 AutomationControl::get_value() const
88 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
89 return Control::get_double (from_list, _session.transport_frame());
93 AutomationControl::pre_realtime_queue_stuff (double val, PBD::Controllable::GroupControlDisposition gcd)
95 if (_group && _group->use_me (gcd)) {
96 _group->pre_realtime_queue_stuff (val);
98 do_pre_realtime_queue_stuff (val);
103 AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
109 /* enforce strict double/boolean value mapping */
117 if (check_rt (val, gcd)) {
118 /* change has been queued to take place in an RT context */
122 if (_group && _group->use_me (gcd)) {
123 _group->set_group_value (shared_from_this(), val);
125 actually_set_value (val, gcd);
129 /** Set the value and do the right thing based on automation state
130 * (e.g. record if necessary, etc.)
131 * @param value `user' value
134 AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
136 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (_list);
137 const framepos_t pos = _session.transport_frame();
141 /* We cannot use ::get_value() here since that is virtual, and intended
142 to return a scalar value that in some way reflects the state of the
143 control (with semantics defined by the control itself, since it's
144 internal state may be more complex than can be fully represented by
147 This method's only job is to set the "user_double()" value of the
148 underlying Evoral::Control object, and so we should compare the new
149 value we're being given to the current user_double().
151 Unless ... we're doing automation playback, in which case the
152 current effective value of the control (used to determine if
153 anything has changed) is the one derived from the automation event
159 old_value = Control::user_double();
161 if (al->automation_write ()) {
163 old_value = Control::user_double ();
164 } else if (al->automation_playback()) {
166 old_value = al->eval (pos);
169 old_value = Control::user_double ();
173 Control::set_double (value, pos, to_list);
175 if (old_value != value) {
176 //AutomationType at = (AutomationType) _parameter.type();
177 //std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value
178 //<< " (was " << old_value << ") @ " << this << std::endl;
181 if (!al || !al->automation_playback ()) {
182 _session.set_dirty ();
189 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
191 Control::set_list (list);
192 Changed (true, Controllable::NoGroup);
196 AutomationControl::set_automation_state (AutoState as)
198 if (flags() & NotAutomatable) {
201 if (_list && as != alist()->automation_state()) {
203 const double val = get_value ();
205 alist()->set_automation_state (as);
207 return; // No watch for boolean automation
211 AutomationWatch::instance().add_automation_watch (shared_from_this());
212 } else if (as == Touch) {
213 if (alist()->empty()) {
214 Control::set_double (val, _session.current_start_frame (), true);
215 Control::set_double (val, _session.current_end_frame (), true);
216 Changed (true, Controllable::NoGroup);
219 AutomationWatch::instance().remove_automation_watch (shared_from_this());
221 /* this seems unlikely, but the combination of
222 * a control surface and the mouse could make
223 * it possible to put the control into Touch
224 * mode *while* touching it.
226 AutomationWatch::instance().add_automation_watch (shared_from_this());
229 AutomationWatch::instance().remove_automation_watch (shared_from_this());
235 AutomationControl::set_automation_style (AutoStyle as)
238 alist()->set_automation_style (as);
242 AutomationControl::start_touch(double when)
250 if (alist()->automation_state() == Touch) {
251 /* subtle. aligns the user value with the playback */
252 set_value (get_value (), Controllable::NoGroup);
253 alist()->start_touch (when);
254 if (!_desc.toggled) {
255 AutomationWatch::instance().add_automation_watch (shared_from_this());
263 AutomationControl::stop_touch(bool mark, double when)
267 set_touching (false);
269 if (alist()->automation_state() == Touch) {
270 alist()->stop_touch (mark, when);
271 if (!_desc.toggled) {
272 AutomationWatch::instance().remove_automation_watch (shared_from_this());
280 AutomationControl::commit_transaction (bool did_write)
283 if (alist ()->before ()) {
284 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
285 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
288 alist ()->clear_history ();
293 AutomationControl::internal_to_interface (double val) const
295 if (_desc.integer_step) {
296 // both upper and lower are inclusive.
297 val = (val - lower()) / (1 + upper() - lower());
299 val = (val - lower()) / (upper() - lower());
302 if (_desc.logarithmic) {
304 val = pow (val, 1./2.0);
314 AutomationControl::interface_to_internal (double val) const
316 if (!isfinite_local (val)) {
319 if (_desc.logarithmic) {
323 val = pow (val, 2.0);
327 if (_desc.integer_step) {
328 val = lower() + val * (1 + upper() - lower());
330 val = lower() + val * (upper() - lower());
333 if (val < lower()) val = lower();
334 if (val > upper()) val = upper();
340 AutomationControl::set_group (boost::shared_ptr<ControlGroup> cg)
342 /* this method can only be called by a ControlGroup. We do not need
343 to ensure consistency by calling ControlGroup::remove_control(),
344 since we are guaranteed that the ControlGroup will take care of that
352 AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd)
354 if (!_session.loading() && (flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
355 /* queue change in RT context */
356 _session.set_control (shared_from_this(), val, gcd);