~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to libs/ardour/automatable.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2001,2007 Paul Davis
 
3
 
 
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.
 
8
 
 
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.
 
13
 
 
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.
 
17
 
 
18
*/
 
19
 
 
20
#include <fstream>
 
21
#include <cstdio>
 
22
#include <errno.h>
 
23
 
 
24
#include <glibmm/miscutils.h>
 
25
 
 
26
#include "pbd/error.h"
 
27
 
 
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"
 
36
 
 
37
#include "i18n.h"
 
38
 
 
39
using namespace std;
 
40
using namespace ARDOUR;
 
41
using namespace PBD;
 
42
 
 
43
const string Automatable::xml_node_name = X_("Automation");
 
44
 
 
45
Automatable::Automatable(Session& session)
 
46
        : _a_session(session)
 
47
{
 
48
}
 
49
 
 
50
Automatable::Automatable (const Automatable& other)
 
51
        : ControlSet (other)
 
52
        , _a_session (other._a_session)
 
53
{
 
54
        Glib::Threads::Mutex::Lock lm (other._control_lock);
 
55
 
 
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));
 
58
                add_control (ac);
 
59
        }
 
60
}
 
61
 
 
62
Automatable::~Automatable ()
 
63
{
 
64
        {
 
65
                Glib::Threads::Mutex::Lock lm (_control_lock);
 
66
                
 
67
                for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
 
68
                        boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
 
69
                }
 
70
        }
 
71
}
 
72
 
 
73
int
 
74
Automatable::old_set_automation_state (const XMLNode& node)
 
75
{
 
76
        const XMLProperty *prop;
 
77
 
 
78
        if ((prop = node.property ("path")) != 0) {
 
79
                load_automation (prop->value());
 
80
        } else {
 
81
                warning << _("Automation node has no path property") << endmsg;
 
82
        }
 
83
 
 
84
        return 0;
 
85
}
 
86
 
 
87
int
 
88
Automatable::load_automation (const string& path)
 
89
{
 
90
        string fullpath;
 
91
 
 
92
        if (Glib::path_is_absolute (path)) { // legacy
 
93
                fullpath = path;
 
94
        } else {
 
95
                fullpath = _a_session.automation_dir();
 
96
                fullpath += path;
 
97
        }
 
98
        ifstream in (fullpath.c_str());
 
99
 
 
100
        if (!in) {
 
101
                warning << string_compose(_("cannot open %2 to load automation data (%3)")
 
102
                                , fullpath, strerror (errno)) << endmsg;
 
103
                return 1;
 
104
        }
 
105
 
 
106
        Glib::Threads::Mutex::Lock lm (control_lock());
 
107
        set<Evoral::Parameter> tosave;
 
108
        controls().clear ();
 
109
 
 
110
        while (in) {
 
111
                double when;
 
112
                double value;
 
113
                uint32_t port;
 
114
 
 
115
                in >> port;  if (!in) break;
 
116
                in >> when;  if (!in) goto bad;
 
117
                in >> value; if (!in) goto bad;
 
118
 
 
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);
 
124
        }
 
125
 
 
126
        return 0;
 
127
 
 
128
  bad:
 
129
        error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
 
130
        controls().clear ();
 
131
        return -1;
 
132
}
 
133
 
 
134
void
 
135
Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
 
136
{
 
137
        Evoral::Parameter param = ac->parameter();
 
138
 
 
139
        boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
 
140
        assert (al);
 
141
 
 
142
        al->automation_state_changed.connect_same_thread (
 
143
                _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
 
144
                );
 
145
 
 
146
        ControlSet::add_control (ac);
 
147
        _can_automate_list.insert (param);
 
148
 
 
149
        automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
 
150
}
 
151
 
 
152
string
 
153
Automatable::describe_parameter (Evoral::Parameter param)
 
154
{
 
155
        /* derived classes like PluginInsert should override this */
 
156
 
 
157
        if (param == Evoral::Parameter(GainAutomation)) {
 
158
                return _("Fader");
 
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);
 
167
        } else {
 
168
                return EventTypeMap::instance().to_symbol(param);
 
169
        }
 
170
}
 
171
 
 
172
void
 
173
Automatable::can_automate (Evoral::Parameter what)
 
174
{
 
175
        _can_automate_list.insert (what);
 
176
}
 
177
 
 
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.
 
181
 */
 
182
int
 
183
Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
 
184
{
 
185
        Glib::Threads::Mutex::Lock lm (control_lock());
 
186
 
 
187
        /* Don't clear controls, since some may be special derived Controllable classes */
 
188
 
 
189
        XMLNodeList nlist = node.children();
 
190
        XMLNodeIterator niter;
 
191
 
 
192
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
193
 
 
194
                /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
 
195
                  error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
 
196
                  continue;
 
197
                  }*/
 
198
 
 
199
                if ((*niter)->name() == "AutomationList") {
 
200
 
 
201
                        const XMLProperty* id_prop = (*niter)->property("automation-id");
 
202
 
 
203
                        Evoral::Parameter param = (id_prop
 
204
                                        ? EventTypeMap::instance().new_parameter(id_prop->value())
 
205
                                        : legacy_param);
 
206
 
 
207
                        if (param.type() == NullAutomation) {
 
208
                                warning << "Automation has null type" << endl;
 
209
                                continue;
 
210
                        }
 
211
 
 
212
                        if (!id_prop) {
 
213
                                warning << "AutomationList node without automation-id property, "
 
214
                                        << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
 
215
                        }
 
216
 
 
217
                        boost::shared_ptr<AutomationControl> existing = automation_control (param);
 
218
 
 
219
                        if (existing) {
 
220
                                existing->alist()->set_state (**niter, 3000);
 
221
                        } else {
 
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);
 
226
                        }
 
227
 
 
228
                } else {
 
229
                        error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
 
230
                }
 
231
        }
 
232
 
 
233
        return 0;
 
234
}
 
235
 
 
236
XMLNode&
 
237
Automatable::get_automation_xml_state ()
 
238
{
 
239
        Glib::Threads::Mutex::Lock lm (control_lock());
 
240
        XMLNode* node = new XMLNode (Automatable::xml_node_name);
 
241
 
 
242
        if (controls().empty()) {
 
243
                return *node;
 
244
        }
 
245
 
 
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());
 
248
                if (!l->empty()) {
 
249
                        node->add_child_nocopy (l->get_state ());
 
250
                }
 
251
        }
 
252
 
 
253
        return *node;
 
254
}
 
255
 
 
256
void
 
257
Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
 
258
{
 
259
        Glib::Threads::Mutex::Lock lm (control_lock());
 
260
 
 
261
        boost::shared_ptr<AutomationControl> c = automation_control (param, true);
 
262
 
 
263
        if (c && (s != c->automation_state())) {
 
264
                c->set_automation_state (s);
 
265
                _a_session.set_dirty ();
 
266
        }
 
267
}
 
268
 
 
269
AutoState
 
270
Automatable::get_parameter_automation_state (Evoral::Parameter param)
 
271
{
 
272
        AutoState result = Off;
 
273
 
 
274
        boost::shared_ptr<AutomationControl> c = automation_control(param);
 
275
        
 
276
        if (c) {
 
277
                result = c->automation_state();
 
278
        }
 
279
 
 
280
        return result;
 
281
}
 
282
 
 
283
void
 
284
Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
 
285
{
 
286
        Glib::Threads::Mutex::Lock lm (control_lock());
 
287
 
 
288
        boost::shared_ptr<AutomationControl> c = automation_control(param, true);
 
289
 
 
290
        if (c && (s != c->automation_style())) {
 
291
                c->set_automation_style (s);
 
292
                _a_session.set_dirty ();
 
293
        }
 
294
}
 
295
 
 
296
AutoStyle
 
297
Automatable::get_parameter_automation_style (Evoral::Parameter param)
 
298
{
 
299
        Glib::Threads::Mutex::Lock lm (control_lock());
 
300
 
 
301
        boost::shared_ptr<Evoral::Control> c = control(param);
 
302
        boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
303
 
 
304
        if (c) {
 
305
                return l->automation_style();
 
306
        } else {
 
307
                return Absolute; // whatever
 
308
        }
 
309
}
 
310
 
 
311
void
 
312
Automatable::protect_automation ()
 
313
{
 
314
        typedef set<Evoral::Parameter> ParameterSet;
 
315
        const ParameterSet& automated_params = what_can_be_automated ();
 
316
 
 
317
        for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
 
318
 
 
319
                boost::shared_ptr<Evoral::Control> c = control(*i);
 
320
                boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
321
 
 
322
                switch (l->automation_state()) {
 
323
                case Write:
 
324
                        l->set_automation_state (Off);
 
325
                        break;
 
326
                case Touch:
 
327
                        l->set_automation_state (Play);
 
328
                        break;
 
329
                default:
 
330
                        break;
 
331
                }
 
332
        }
 
333
}
 
334
 
 
335
void
 
336
Automatable::transport_located (framepos_t now)
 
337
{
 
338
        for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
 
339
 
 
340
                boost::shared_ptr<AutomationControl> c
 
341
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
 
342
                if (c) {
 
343
                        boost::shared_ptr<AutomationList> l
 
344
                                = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
345
 
 
346
                        if (l) {
 
347
                                l->start_write_pass (now);
 
348
                        }
 
349
                }
 
350
        }
 
351
}
 
352
 
 
353
void
 
354
Automatable::transport_stopped (framepos_t now)
 
355
{
 
356
        for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
 
357
 
 
358
                boost::shared_ptr<AutomationControl> c
 
359
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
 
360
                if (c) {
 
361
                        boost::shared_ptr<AutomationList> l
 
362
                                = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
363
 
 
364
                        if (l) {
 
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.
 
370
                                */
 
371
                                l->stop_touch (true, now);
 
372
                                l->write_pass_finished (now);
 
373
 
 
374
                                if (l->automation_playback()) {
 
375
                                        c->set_value(c->list()->eval(now));
 
376
                                }
 
377
 
 
378
                                if (l->automation_state() == Write) {
 
379
                                        l->set_automation_state (Touch);
 
380
                                }
 
381
                        }
 
382
                }
 
383
        }
 
384
}
 
385
 
 
386
boost::shared_ptr<Evoral::Control>
 
387
Automatable::control_factory(const Evoral::Parameter& param)
 
388
{
 
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);
 
393
                if (mt) {
 
394
                        control = new MidiTrack::MidiControl(mt, param);
 
395
                } else {
 
396
                        warning << "MidiCCAutomation for non-MidiTrack" << endl;
 
397
                }
 
398
        } else if (param.type() == PluginAutomation) {
 
399
                PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
 
400
                if (pi) {
 
401
                        control = new PluginInsert::PluginControl(pi, param);
 
402
                } else {
 
403
                        warning << "PluginAutomation for non-Plugin" << endl;
 
404
                }
 
405
        } else if (param.type() == GainAutomation) {
 
406
                Amp* amp = dynamic_cast<Amp*>(this);
 
407
                if (amp) {
 
408
                        control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
 
409
                } else {
 
410
                        warning << "GainAutomation for non-Amp" << endl;
 
411
                }
 
412
        } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
 
413
                Pannable* pannable = dynamic_cast<Pannable*>(this);
 
414
                if (pannable) {
 
415
                        control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
 
416
                } else {
 
417
                        warning << "PanAutomation for non-Pannable" << endl;
 
418
                }
 
419
        }
 
420
 
 
421
        if (!control) {
 
422
                control = new AutomationControl(_a_session, param);
 
423
        }
 
424
 
 
425
        control->set_list(list);
 
426
        return boost::shared_ptr<Evoral::Control>(control);
 
427
}
 
428
 
 
429
boost::shared_ptr<AutomationControl>
 
430
Automatable::automation_control (const Evoral::Parameter& id, bool create)
 
431
{
 
432
        return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
 
433
}
 
434
 
 
435
boost::shared_ptr<const AutomationControl>
 
436
Automatable::automation_control (const Evoral::Parameter& id) const
 
437
{
 
438
        return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
 
439
}
 
440
 
 
441
void
 
442
Automatable::clear_controls ()
 
443
{
 
444
        _control_connections.drop_connections ();
 
445
        ControlSet::clear_controls ();
 
446
}
 
447
 
 
448
string
 
449
Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
 
450
{
 
451
        std::stringstream s;
 
452
 
 
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
 
455
           in different ways.
 
456
        */
 
457
 
 
458
        // Hack to display CC as integer value, rather than double
 
459
        if (ac->parameter().type() == MidiCCAutomation) {
 
460
                s << lrint (ac->get_value());
 
461
        } else {
 
462
                s << std::fixed << std::setprecision(3) << ac->get_value();
 
463
        }
 
464
 
 
465
        return s.str ();
 
466
}