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::get_save_value() const
98 /* save user-value, not incl masters */
99 return Control::get_double ();
103 AutomationControl::pre_realtime_queue_stuff (double val, PBD::Controllable::GroupControlDisposition gcd)
105 if (_group && _group->use_me (gcd)) {
106 _group->pre_realtime_queue_stuff (val);
108 do_pre_realtime_queue_stuff (val);
113 AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
119 /* enforce strict double/boolean value mapping */
127 if (check_rt (val, gcd)) {
128 /* change has been queued to take place in an RT context */
132 if (_group && _group->use_me (gcd)) {
133 _group->set_group_value (shared_from_this(), val);
135 actually_set_value (val, gcd);
140 AutomationControl::grouped_controls () const
142 if (_group && _group->use_me (PBD::Controllable::UseGroup)) {
143 return _group->controls ();
145 return ControlList ();
150 AutomationControl::automation_run (framepos_t start, pframes_t nframes)
152 if (!automation_playback ()) {
158 double val = _list->rt_safe_eval (start, valid);
163 const double thresh = .5 * (_desc.upper - _desc.lower);
164 set_value_unchecked (val >= thresh ? _desc.upper : _desc.lower);
166 set_value_unchecked (val);
170 /** Set the value and do the right thing based on automation state
171 * (e.g. record if necessary, etc.)
172 * @param value `user' value
175 AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
177 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (_list);
178 const framepos_t pos = _session.transport_frame();
181 /* We cannot use ::get_value() here since that is virtual, and intended
182 to return a scalar value that in some way reflects the state of the
183 control (with semantics defined by the control itself, since it's
184 internal state may be more complex than can be fully represented by
187 This method's only job is to set the "user_double()" value of the
188 underlying Evoral::Control object, and so we should compare the new
189 value we're being given to the current user_double().
191 Unless ... we're doing automation playback, in which case the
192 current effective value of the control (used to determine if
193 anything has changed) is the one derived from the automation event
196 float old_value = Control::user_double();
198 if (al && al->automation_write ()) {
204 Control::set_double (value, pos, to_list);
206 if (old_value != (float)value) {
208 AutomationType at = (AutomationType) _parameter.type();
209 std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value
210 << " (was " << old_value << ") @ " << this << std::endl;
214 if (!al || !al->automation_playback ()) {
215 _session.set_dirty ();
221 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
223 Control::set_list (list);
224 Changed (true, Controllable::NoGroup);
228 AutomationControl::set_automation_state (AutoState as)
230 if (flags() & NotAutomatable) {
233 if (_list && as != alist()->automation_state()) {
235 const double val = get_value ();
237 alist()->set_automation_state (as);
239 Changed (false, Controllable::NoGroup); // notify slaves, update boolean masters
240 return; // No watch for boolean automation
244 AutomationWatch::instance().add_automation_watch (shared_from_this());
245 } else if (as == Touch) {
246 if (alist()->empty()) {
247 Control::set_double (val, _session.current_start_frame (), true);
248 Control::set_double (val, _session.current_end_frame (), true);
249 Changed (true, Controllable::NoGroup);
252 AutomationWatch::instance().remove_automation_watch (shared_from_this());
254 /* this seems unlikely, but the combination of
255 * a control surface and the mouse could make
256 * it possible to put the control into Touch
257 * mode *while* touching it.
259 AutomationWatch::instance().add_automation_watch (shared_from_this());
262 AutomationWatch::instance().remove_automation_watch (shared_from_this());
268 AutomationControl::start_touch(double when)
275 if (alist()->automation_state() == Touch) {
276 /* subtle. aligns the user value with the playback and
277 * use take actual value (incl masters).
279 * Touch + hold writes inverse curve of master-automation
280 * using AutomationWatch::timer ()
282 AutomationControl::actually_set_value (get_value (), Controllable::NoGroup);
283 alist()->start_touch (when);
284 if (!_desc.toggled) {
285 AutomationWatch::instance().add_automation_watch (shared_from_this());
293 AutomationControl::stop_touch(bool mark, double when)
297 set_touching (false);
299 if (alist()->automation_state() == Touch) {
300 alist()->stop_touch (mark, when);
301 if (!_desc.toggled) {
302 AutomationWatch::instance().remove_automation_watch (shared_from_this());
310 AutomationControl::commit_transaction (bool did_write)
313 if (alist ()->before ()) {
314 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
315 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
318 alist ()->clear_history ();
322 /* take control-value and return UI range [0..1] */
324 AutomationControl::internal_to_interface (double val) const
326 // XXX maybe optimize. _desc.from_interface() has
327 // a switch-statement depending on AutomationType.
328 return _desc.to_interface (val);
331 /* map GUI range [0..1] to control-value */
333 AutomationControl::interface_to_internal (double val) const
335 if (!isfinite_local (val)) {
339 // XXX maybe optimize. see above.
340 return _desc.from_interface (val);
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);