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.
24
#include <glibmm/miscutils.h>
26
#include "pbd/error.h"
28
#include "ardour/amp.h"
29
#include "ardour/automatable.h"
30
#include "ardour/event_type_map.h"
31
#include "ardour/midi_track.h"
32
#include "ardour/pan_controllable.h"
33
#include "ardour/pannable.h"
34
#include "ardour/plugin_insert.h"
35
#include "ardour/session.h"
40
using namespace ARDOUR;
43
const string Automatable::xml_node_name = X_("Automation");
45
Automatable::Automatable(Session& session)
50
Automatable::Automatable (const Automatable& other)
52
, _a_session (other._a_session)
54
Glib::Threads::Mutex::Lock lm (other._control_lock);
56
for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
57
boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
62
Automatable::~Automatable ()
65
Glib::Threads::Mutex::Lock lm (_control_lock);
67
for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
68
boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
74
Automatable::old_set_automation_state (const XMLNode& node)
76
const XMLProperty *prop;
78
if ((prop = node.property ("path")) != 0) {
79
load_automation (prop->value());
81
warning << _("Automation node has no path property") << endmsg;
88
Automatable::load_automation (const string& path)
92
if (Glib::path_is_absolute (path)) { // legacy
95
fullpath = _a_session.automation_dir();
98
ifstream in (fullpath.c_str());
101
warning << string_compose(_("cannot open %2 to load automation data (%3)")
102
, fullpath, strerror (errno)) << endmsg;
106
Glib::Threads::Mutex::Lock lm (control_lock());
107
set<Evoral::Parameter> tosave;
115
in >> port; if (!in) break;
116
in >> when; if (!in) goto bad;
117
in >> value; if (!in) goto bad;
119
Evoral::Parameter param(PluginAutomation, 0, port);
120
/* FIXME: this is legacy and only used for plugin inserts? I think? */
121
boost::shared_ptr<Evoral::Control> c = control (param, true);
122
c->list()->add (when, value);
123
tosave.insert (param);
129
error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
135
Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
137
Evoral::Parameter param = ac->parameter();
139
boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
142
al->automation_state_changed.connect_same_thread (
143
_list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
146
ControlSet::add_control (ac);
147
_can_automate_list.insert (param);
149
automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
153
Automatable::describe_parameter (Evoral::Parameter param)
155
/* derived classes like PluginInsert should override this */
157
if (param == Evoral::Parameter(GainAutomation)) {
159
} else if (param.type() == MidiCCAutomation) {
160
return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
161
} else if (param.type() == MidiPgmChangeAutomation) {
162
return string_compose("Program [%1]", int(param.channel()) + 1);
163
} else if (param.type() == MidiPitchBenderAutomation) {
164
return string_compose("Bender [%1]", int(param.channel()) + 1);
165
} else if (param.type() == MidiChannelPressureAutomation) {
166
return string_compose("Pressure [%1]", int(param.channel()) + 1);
168
return EventTypeMap::instance().to_symbol(param);
173
Automatable::can_automate (Evoral::Parameter what)
175
_can_automate_list.insert (what);
178
/** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
179
* had a single automation parameter, with it's type implicit. Derived objects should
180
* pass that type and it will be used for the untyped AutomationList found.
183
Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
185
Glib::Threads::Mutex::Lock lm (control_lock());
187
/* Don't clear controls, since some may be special derived Controllable classes */
189
XMLNodeList nlist = node.children();
190
XMLNodeIterator niter;
192
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
194
/*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
195
error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
199
if ((*niter)->name() == "AutomationList") {
201
const XMLProperty* id_prop = (*niter)->property("automation-id");
203
Evoral::Parameter param = (id_prop
204
? EventTypeMap::instance().new_parameter(id_prop->value())
207
if (param.type() == NullAutomation) {
208
warning << "Automation has null type" << endl;
213
warning << "AutomationList node without automation-id property, "
214
<< "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
217
boost::shared_ptr<AutomationControl> existing = automation_control (param);
220
existing->alist()->set_state (**niter, 3000);
222
boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
223
add_control (newcontrol);
224
boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
225
newcontrol->set_list(al);
229
error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
237
Automatable::get_automation_xml_state ()
239
Glib::Threads::Mutex::Lock lm (control_lock());
240
XMLNode* node = new XMLNode (Automatable::xml_node_name);
242
if (controls().empty()) {
246
for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
247
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
249
node->add_child_nocopy (l->get_state ());
257
Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
259
Glib::Threads::Mutex::Lock lm (control_lock());
261
boost::shared_ptr<AutomationControl> c = automation_control (param, true);
263
if (c && (s != c->automation_state())) {
264
c->set_automation_state (s);
265
_a_session.set_dirty ();
270
Automatable::get_parameter_automation_state (Evoral::Parameter param)
272
AutoState result = Off;
274
boost::shared_ptr<AutomationControl> c = automation_control(param);
277
result = c->automation_state();
284
Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
286
Glib::Threads::Mutex::Lock lm (control_lock());
288
boost::shared_ptr<AutomationControl> c = automation_control(param, true);
290
if (c && (s != c->automation_style())) {
291
c->set_automation_style (s);
292
_a_session.set_dirty ();
297
Automatable::get_parameter_automation_style (Evoral::Parameter param)
299
Glib::Threads::Mutex::Lock lm (control_lock());
301
boost::shared_ptr<Evoral::Control> c = control(param);
302
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
305
return l->automation_style();
307
return Absolute; // whatever
312
Automatable::protect_automation ()
314
typedef set<Evoral::Parameter> ParameterSet;
315
const ParameterSet& automated_params = what_can_be_automated ();
317
for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
319
boost::shared_ptr<Evoral::Control> c = control(*i);
320
boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
322
switch (l->automation_state()) {
324
l->set_automation_state (Off);
327
l->set_automation_state (Play);
336
Automatable::transport_located (framepos_t now)
338
for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
340
boost::shared_ptr<AutomationControl> c
341
= boost::dynamic_pointer_cast<AutomationControl>(li->second);
343
boost::shared_ptr<AutomationList> l
344
= boost::dynamic_pointer_cast<AutomationList>(c->list());
347
l->start_write_pass (now);
354
Automatable::transport_stopped (framepos_t now)
356
for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
358
boost::shared_ptr<AutomationControl> c
359
= boost::dynamic_pointer_cast<AutomationControl>(li->second);
361
boost::shared_ptr<AutomationList> l
362
= boost::dynamic_pointer_cast<AutomationList>(c->list());
365
/* Stop any active touch gesture just before we mark the write pass
366
as finished. If we don't do this, the transport can end up stopped with
367
an AutomationList thinking that a touch is still in progress and,
368
when the transport is re-started, a touch will magically
369
be happening without it ever have being started in the usual way.
371
l->stop_touch (true, now);
372
l->write_pass_finished (now);
374
if (l->automation_playback()) {
375
c->set_value(c->list()->eval(now));
378
if (l->automation_state() == Write) {
379
l->set_automation_state (Touch);
386
boost::shared_ptr<Evoral::Control>
387
Automatable::control_factory(const Evoral::Parameter& param)
389
boost::shared_ptr<AutomationList> list(new AutomationList(param));
390
Evoral::Control* control = NULL;
391
if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
392
MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
394
control = new MidiTrack::MidiControl(mt, param);
396
warning << "MidiCCAutomation for non-MidiTrack" << endl;
398
} else if (param.type() == PluginAutomation) {
399
PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
401
control = new PluginInsert::PluginControl(pi, param);
403
warning << "PluginAutomation for non-Plugin" << endl;
405
} else if (param.type() == GainAutomation) {
406
Amp* amp = dynamic_cast<Amp*>(this);
408
control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
410
warning << "GainAutomation for non-Amp" << endl;
412
} else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
413
Pannable* pannable = dynamic_cast<Pannable*>(this);
415
control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
417
warning << "PanAutomation for non-Pannable" << endl;
422
control = new AutomationControl(_a_session, param);
425
control->set_list(list);
426
return boost::shared_ptr<Evoral::Control>(control);
429
boost::shared_ptr<AutomationControl>
430
Automatable::automation_control (const Evoral::Parameter& id, bool create)
432
return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
435
boost::shared_ptr<const AutomationControl>
436
Automatable::automation_control (const Evoral::Parameter& id) const
438
return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
442
Automatable::clear_controls ()
444
_control_connections.drop_connections ();
445
ControlSet::clear_controls ();
449
Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
453
/* this is a the default fallback for this virtual method. Derived Automatables
454
are free to override this to display the values of their parameters/controls
458
// Hack to display CC as integer value, rather than double
459
if (ac->parameter().type() == MidiCCAutomation) {
460
s << lrint (ac->get_value());
462
s << std::fixed << std::setprecision(3) << ac->get_value();