2 Copyright (C) 2001,2007 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "ardour/ardour.h"
26 #include <glibmm/miscutils.h>
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/stacktrace.h"
32 #include "midi++/names.h"
34 #include "ardour/automatable.h"
35 #include "ardour/amp.h"
36 #include "ardour/event_type_map.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/pannable.h"
39 #include "ardour/panner.h"
40 #include "ardour/pan_controllable.h"
41 #include "ardour/plugin_insert.h"
42 #include "ardour/session.h"
47 using namespace ARDOUR
;
50 framecnt_t
Automatable::_automation_interval
= 0;
51 const string
Automatable::xml_node_name
= X_("Automation");
53 Automatable::Automatable(Session
& session
)
55 , _last_automation_snapshot(0)
59 Automatable::Automatable (const Automatable
& other
)
61 , _a_session (other
._a_session
)
62 , _last_automation_snapshot (0)
64 Glib::Mutex::Lock
lm (other
._control_lock
);
66 for (Controls::const_iterator i
= other
._controls
.begin(); i
!= other
._controls
.end(); ++i
) {
67 boost::shared_ptr
<Evoral::Control
> ac (control_factory (i
->first
));
72 Automatable::old_set_automation_state (const XMLNode
& node
)
74 const XMLProperty
*prop
;
76 if ((prop
= node
.property ("path")) != 0) {
77 load_automation (prop
->value());
79 warning
<< _("Automation node has no path property") << endmsg
;
82 if ((prop
= node
.property ("visible")) != 0) {
86 _visible_controls
.clear ();
88 sstr
<< prop
->value();
94 mark_automation_visible (Evoral::Parameter(PluginAutomation
, 0, what
), true);
98 _last_automation_snapshot
= 0;
104 Automatable::load_automation (const string
& path
)
108 if (Glib::path_is_absolute (path
)) { // legacy
111 fullpath
= _a_session
.automation_dir();
114 ifstream
in (fullpath
.c_str());
117 warning
<< string_compose(_("cannot open %2 to load automation data (%3)")
118 , fullpath
, strerror (errno
)) << endmsg
;
122 Glib::Mutex::Lock
lm (control_lock());
123 set
<Evoral::Parameter
> tosave
;
126 _last_automation_snapshot
= 0;
133 in
>> port
; if (!in
) break;
134 in
>> when
; if (!in
) goto bad
;
135 in
>> value
; if (!in
) goto bad
;
137 Evoral::Parameter
param(PluginAutomation
, 0, port
);
138 /* FIXME: this is legacy and only used for plugin inserts? I think? */
139 boost::shared_ptr
<Evoral::Control
> c
= control (param
, true);
140 c
->list()->add (when
, value
);
141 tosave
.insert (param
);
147 error
<< string_compose(_("cannot load automation data from %2"), fullpath
) << endmsg
;
153 Automatable::add_control(boost::shared_ptr
<Evoral::Control
> ac
)
155 Evoral::Parameter param
= ac
->parameter();
157 boost::shared_ptr
<AutomationList
> al
= boost::dynamic_pointer_cast
<AutomationList
> (ac
->list ());
160 al
->automation_state_changed
.connect_same_thread (
161 _list_connections
, boost::bind (&Automatable::automation_list_automation_state_changed
, this, ac
->parameter(), _1
)
164 ControlSet::add_control (ac
);
165 _can_automate_list
.insert (param
);
167 automation_list_automation_state_changed (param
, al
->automation_state ()); // sync everything up
171 Automatable::what_has_visible_data(set
<Evoral::Parameter
>& s
) const
173 Glib::Mutex::Lock
lm (control_lock());
174 set
<Evoral::Parameter
>::const_iterator li
;
176 for (li
= _visible_controls
.begin(); li
!= _visible_controls
.end(); ++li
) {
182 Automatable::describe_parameter (Evoral::Parameter param
)
184 /* derived classes like PluginInsert should override this */
186 if (param
== Evoral::Parameter(GainAutomation
)) {
188 } else if (param
.type() == MidiCCAutomation
) {
189 return string_compose("%1: %2 [%3]",
190 param
.id(), midi_name(param
.id()), int(param
.channel()) + 1);
191 } else if (param
.type() == MidiPgmChangeAutomation
) {
192 return string_compose("Program [%1]", int(param
.channel()) + 1);
193 } else if (param
.type() == MidiPitchBenderAutomation
) {
194 return string_compose("Bender [%1]", int(param
.channel()) + 1);
195 } else if (param
.type() == MidiChannelPressureAutomation
) {
196 return string_compose("Pressure [%1]", int(param
.channel()) + 1);
198 return EventTypeMap::instance().to_symbol(param
);
203 Automatable::can_automate (Evoral::Parameter what
)
205 _can_automate_list
.insert (what
);
209 Automatable::mark_automation_visible (Evoral::Parameter what
, bool yn
)
212 _visible_controls
.insert (what
);
214 set
<Evoral::Parameter
>::iterator i
;
216 if ((i
= _visible_controls
.find (what
)) != _visible_controls
.end()) {
217 _visible_controls
.erase (i
);
222 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
223 * had a single automation parameter, with it's type implicit. Derived objects should
224 * pass that type and it will be used for the untyped AutomationList found.
227 Automatable::set_automation_xml_state (const XMLNode
& node
, Evoral::Parameter legacy_param
)
229 Glib::Mutex::Lock
lm (control_lock());
231 /* Don't clear controls, since some may be special derived Controllable classes */
233 _visible_controls
.clear ();
235 XMLNodeList nlist
= node
.children();
236 XMLNodeIterator niter
;
238 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
240 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
241 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
245 if ((*niter
)->name() == "AutomationList") {
247 const XMLProperty
* id_prop
= (*niter
)->property("automation-id");
249 Evoral::Parameter param
= (id_prop
250 ? EventTypeMap::instance().new_parameter(id_prop
->value())
253 if (param
.type() == NullAutomation
) {
254 warning
<< "Automation has null type" << endl
;
261 warning
<< "AutomationList node without automation-id property, "
262 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param
) << endmsg
;
265 boost::shared_ptr
<AutomationControl
> existing
= automation_control (param
);
268 existing
->alist()->set_state (**niter
, 3000);
270 boost::shared_ptr
<Evoral::Control
> newcontrol
= control_factory(param
);
271 add_control (newcontrol
);
272 boost::shared_ptr
<AutomationList
> al (new AutomationList(**niter
, param
));
273 newcontrol
->set_list(al
);
277 error
<< "Expected AutomationList node, got '" << (*niter
)->name() << "'" << endmsg
;
281 _last_automation_snapshot
= 0;
287 Automatable::get_automation_xml_state ()
289 Glib::Mutex::Lock
lm (control_lock());
290 XMLNode
* node
= new XMLNode (Automatable::xml_node_name
);
292 if (controls().empty()) {
296 for (Controls::iterator li
= controls().begin(); li
!= controls().end(); ++li
) {
297 boost::shared_ptr
<AutomationList
> l
298 = boost::dynamic_pointer_cast
<AutomationList
>(li
->second
->list());
300 node
->add_child_nocopy (l
->get_state ());
308 Automatable::set_parameter_automation_state (Evoral::Parameter param
, AutoState s
)
310 Glib::Mutex::Lock
lm (control_lock());
312 boost::shared_ptr
<Evoral::Control
> c
= control (param
, true);
313 boost::shared_ptr
<AutomationList
> l
= boost::dynamic_pointer_cast
<AutomationList
>(c
->list());
315 if (s
!= l
->automation_state()) {
316 l
->set_automation_state (s
);
317 _a_session
.set_dirty ();
322 Automatable::get_parameter_automation_state (Evoral::Parameter param
)
324 AutoState result
= Off
;
326 boost::shared_ptr
<Evoral::Control
> c
= control(param
);
327 boost::shared_ptr
<AutomationList
> l
= boost::dynamic_pointer_cast
<AutomationList
>(c
->list());
330 result
= l
->automation_state();
337 Automatable::set_parameter_automation_style (Evoral::Parameter param
, AutoStyle s
)
339 Glib::Mutex::Lock
lm (control_lock());
341 boost::shared_ptr
<Evoral::Control
> c
= control(param
, true);
342 boost::shared_ptr
<AutomationList
> l
= boost::dynamic_pointer_cast
<AutomationList
>(c
->list());
344 if (s
!= l
->automation_style()) {
345 l
->set_automation_style (s
);
346 _a_session
.set_dirty ();
351 Automatable::get_parameter_automation_style (Evoral::Parameter param
)
353 Glib::Mutex::Lock
lm (control_lock());
355 boost::shared_ptr
<Evoral::Control
> c
= control(param
);
356 boost::shared_ptr
<AutomationList
> l
= boost::dynamic_pointer_cast
<AutomationList
>(c
->list());
359 return l
->automation_style();
361 return Absolute
; // whatever
366 Automatable::protect_automation ()
368 typedef set
<Evoral::Parameter
> ParameterSet
;
369 ParameterSet automated_params
;
371 what_has_data(automated_params
);
373 for (ParameterSet::iterator i
= automated_params
.begin(); i
!= automated_params
.end(); ++i
) {
375 boost::shared_ptr
<Evoral::Control
> c
= control(*i
);
376 boost::shared_ptr
<AutomationList
> l
= boost::dynamic_pointer_cast
<AutomationList
>(c
->list());
378 switch (l
->automation_state()) {
380 l
->set_automation_state (Off
);
383 l
->set_automation_state (Play
);
392 Automatable::automation_snapshot (framepos_t now
, bool force
)
394 if (force
|| _last_automation_snapshot
> now
|| (now
- _last_automation_snapshot
) > _automation_interval
) {
396 for (Controls::iterator i
= controls().begin(); i
!= controls().end(); ++i
) {
397 boost::shared_ptr
<AutomationControl
> c
398 = boost::dynamic_pointer_cast
<AutomationControl
>(i
->second
);
399 if (_a_session
.transport_rolling() && c
->automation_write()) {
400 c
->list()->rt_add (now
, i
->second
->user_double());
404 _last_automation_snapshot
= now
;
409 Automatable::transport_stopped (framepos_t now
)
411 for (Controls::iterator li
= controls().begin(); li
!= controls().end(); ++li
) {
413 boost::shared_ptr
<AutomationControl
> c
414 = boost::dynamic_pointer_cast
<AutomationControl
>(li
->second
);
416 boost::shared_ptr
<AutomationList
> l
417 = boost::dynamic_pointer_cast
<AutomationList
>(c
->list());
420 /* Stop any active touch gesture just before we mark the write pass
421 as finished. If we don't do this, the transport can end up stopped with
422 an AutomationList thinking that a touch is still in progress and,
423 when the transport is re-started, a touch will magically
424 be happening without it ever have being started in the usual way.
426 l
->stop_touch (true, now
);
427 l
->write_pass_finished (now
);
429 if (l
->automation_playback()) {
430 c
->set_value(c
->list()->eval(now
));
433 if (l
->automation_state() == Write
) {
434 l
->set_automation_state (Touch
);
441 boost::shared_ptr
<Evoral::Control
>
442 Automatable::control_factory(const Evoral::Parameter
& param
)
444 boost::shared_ptr
<AutomationList
> list(new AutomationList(param
));
445 Evoral::Control
* control
= NULL
;
446 if (param
.type() >= MidiCCAutomation
&& param
.type() <= MidiChannelPressureAutomation
) {
447 MidiTrack
* mt
= dynamic_cast<MidiTrack
*>(this);
449 control
= new MidiTrack::MidiControl(mt
, param
);
451 warning
<< "MidiCCAutomation for non-MidiTrack" << endl
;
453 } else if (param
.type() == PluginAutomation
) {
454 PluginInsert
* pi
= dynamic_cast<PluginInsert
*>(this);
456 control
= new PluginInsert::PluginControl(pi
, param
);
458 warning
<< "PluginAutomation for non-Plugin" << endl
;
460 } else if (param
.type() == GainAutomation
) {
461 Amp
* amp
= dynamic_cast<Amp
*>(this);
463 control
= new Amp::GainControl(X_("gaincontrol"), _a_session
, amp
, param
);
465 warning
<< "GainAutomation for non-Amp" << endl
;
467 } else if (param
.type() == PanAzimuthAutomation
|| param
.type() == PanWidthAutomation
|| param
.type() == PanElevationAutomation
) {
468 Pannable
* pannable
= dynamic_cast<Pannable
*>(this);
470 control
= new PanControllable (_a_session
, pannable
->describe_parameter (param
), pannable
, param
);
472 warning
<< "PanAutomation for non-Pannable" << endl
;
477 control
= new AutomationControl(_a_session
, param
);
480 control
->set_list(list
);
481 return boost::shared_ptr
<Evoral::Control
>(control
);
484 boost::shared_ptr
<AutomationControl
>
485 Automatable::automation_control (const Evoral::Parameter
& id
, bool create
)
487 return boost::dynamic_pointer_cast
<AutomationControl
>(Evoral::ControlSet::control(id
, create
));
490 boost::shared_ptr
<const AutomationControl
>
491 Automatable::automation_control (const Evoral::Parameter
& id
) const
493 return boost::dynamic_pointer_cast
<const AutomationControl
>(Evoral::ControlSet::control(id
));
497 Automatable::clear_controls ()
499 _control_connections
.drop_connections ();
500 ControlSet::clear_controls ();
504 Automatable::value_as_string (boost::shared_ptr
<AutomationControl
> ac
) const
508 /* this is a the default fallback for this virtual method. Derived Automatables
509 are free to override this to display the values of their parameters/controls
513 // Hack to display CC as integer value, rather than double
514 if (ac
->parameter().type() == MidiCCAutomation
) {
515 s
<< lrint (ac
->get_value());
517 s
<< std::fixed
<< std::setprecision(3) << ac
->get_value();