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"
39 // C99 'isfinite()' is not available in MSVC.
40 #define isfinite_local(val) (bool)_finite((double)val)
42 #define isfinite_local isfinite
46 using namespace ARDOUR;
49 AutomationControl::AutomationControl(ARDOUR::Session& session,
50 const Evoral::Parameter& parameter,
51 const ParameterDescriptor& desc,
52 boost::shared_ptr<ARDOUR::AutomationList> list,
54 Controllable::Flag flags)
56 : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name, flags)
57 , Evoral::Control(parameter, desc, list)
62 set_flags (Controllable::Toggle);
64 boost::shared_ptr<AutomationList> al = alist();
66 al->StateChanged.connect_same_thread (_state_changed_connection, boost::bind (&Session::set_dirty, &_session));
70 AutomationControl::~AutomationControl ()
72 _session.selection().remove_control_by_id (id());
73 DropReferences (); /* EMIT SIGNAL */
77 AutomationControl::writable() const
79 boost::shared_ptr<AutomationList> al = alist();
81 return al->automation_state() != Play;
86 /** Get the current effective `user' value based on automation state */
88 AutomationControl::get_value() const
90 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
91 return Control::get_double (from_list, _session.transport_frame());
95 AutomationControl::pre_realtime_queue_stuff (double val, PBD::Controllable::GroupControlDisposition gcd)
97 if (_group && _group->use_me (gcd)) {
98 _group->pre_realtime_queue_stuff (val);
100 do_pre_realtime_queue_stuff (val);
105 AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
111 /* enforce strict double/boolean value mapping */
119 if (check_rt (val, gcd)) {
120 /* change has been queued to take place in an RT context */
124 if (_group && _group->use_me (gcd)) {
125 _group->set_group_value (shared_from_this(), val);
127 actually_set_value (val, gcd);
131 /** Set the value and do the right thing based on automation state
132 * (e.g. record if necessary, etc.)
133 * @param value `user' value
136 AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
138 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (_list);
139 const framepos_t pos = _session.transport_frame();
143 /* We cannot use ::get_value() here since that is virtual, and intended
144 to return a scalar value that in some way reflects the state of the
145 control (with semantics defined by the control itself, since it's
146 internal state may be more complex than can be fully represented by
149 This method's only job is to set the "user_double()" value of the
150 underlying Evoral::Control object, and so we should compare the new
151 value we're being given to the current user_double().
153 Unless ... we're doing automation playback, in which case the
154 current effective value of the control (used to determine if
155 anything has changed) is the one derived from the automation event
161 old_value = Control::user_double();
163 if (al->automation_write ()) {
165 old_value = Control::user_double ();
166 } else if (al->automation_playback()) {
168 old_value = al->eval (pos);
171 old_value = Control::user_double ();
175 Control::set_double (value, pos, to_list);
177 if (old_value != value) {
178 //AutomationType at = (AutomationType) _parameter.type();
179 //std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value
180 //<< " (was " << old_value << ") @ " << this << std::endl;
183 if (!al || !al->automation_playback ()) {
184 _session.set_dirty ();
191 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
193 Control::set_list (list);
194 Changed (true, Controllable::NoGroup);
198 AutomationControl::set_automation_state (AutoState as)
200 if (flags() & NotAutomatable) {
203 if (_list && as != alist()->automation_state()) {
205 const double val = get_value ();
207 alist()->set_automation_state (as);
209 return; // No watch for boolean automation
213 AutomationWatch::instance().add_automation_watch (shared_from_this());
214 } else if (as == Touch) {
215 if (alist()->empty()) {
216 Control::set_double (val, _session.current_start_frame (), true);
217 Control::set_double (val, _session.current_end_frame (), true);
218 Changed (true, Controllable::NoGroup);
221 AutomationWatch::instance().remove_automation_watch (shared_from_this());
223 /* this seems unlikely, but the combination of
224 * a control surface and the mouse could make
225 * it possible to put the control into Touch
226 * mode *while* touching it.
228 AutomationWatch::instance().add_automation_watch (shared_from_this());
231 AutomationWatch::instance().remove_automation_watch (shared_from_this());
237 AutomationControl::set_automation_style (AutoStyle as)
240 alist()->set_automation_style (as);
244 AutomationControl::start_touch(double when)
252 if (alist()->automation_state() == Touch) {
253 /* subtle. aligns the user value with the playback */
254 set_value (get_value (), Controllable::NoGroup);
255 alist()->start_touch (when);
256 if (!_desc.toggled) {
257 AutomationWatch::instance().add_automation_watch (shared_from_this());
265 AutomationControl::stop_touch(bool mark, double when)
269 set_touching (false);
271 if (alist()->automation_state() == Touch) {
272 alist()->stop_touch (mark, when);
273 if (!_desc.toggled) {
274 AutomationWatch::instance().remove_automation_watch (shared_from_this());
282 AutomationControl::commit_transaction (bool did_write)
285 if (alist ()->before ()) {
286 _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
287 _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
290 alist ()->clear_history ();
295 AutomationControl::internal_to_interface (double val) const
297 if (_desc.integer_step) {
298 // both upper and lower are inclusive.
299 val = (val - lower()) / (1 + upper() - lower());
301 val = (val - lower()) / (upper() - lower());
304 if (_desc.logarithmic) {
306 val = pow (val, 1./2.0);
316 AutomationControl::interface_to_internal (double val) const
318 if (!isfinite_local (val)) {
321 if (_desc.logarithmic) {
325 val = pow (val, 2.0);
329 if (_desc.integer_step) {
330 val = lower() + val * (1 + upper() - lower());
332 val = lower() + val * (upper() - lower());
335 if (val < lower()) val = lower();
336 if (val > upper()) val = upper();
342 AutomationControl::set_group (boost::shared_ptr<ControlGroup> cg)
344 /* this method can only be called by a ControlGroup. We do not need
345 to ensure consistency by calling ControlGroup::remove_control(),
346 since we are guaranteed that the ControlGroup will take care of that
354 AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd)
356 if (!_session.loading() && (flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
357 /* queue change in RT context */
358 _session.set_control (shared_from_this(), val, gcd);