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

« back to all changes in this revision

Viewing changes to libs/ardour/automation_list.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) 2002 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 <set>
 
21
#include <climits>
 
22
#include <float.h>
 
23
#include <cmath>
 
24
#include <sstream>
 
25
#include <algorithm>
 
26
#include "ardour/automation_list.h"
 
27
#include "ardour/event_type_map.h"
 
28
#include "evoral/Curve.hpp"
 
29
#include "pbd/stacktrace.h"
 
30
#include "pbd/enumwriter.h"
 
31
 
 
32
#include "i18n.h"
 
33
 
 
34
using namespace std;
 
35
using namespace ARDOUR;
 
36
using namespace PBD;
 
37
 
 
38
PBD::Signal1<void,AutomationList *> AutomationList::AutomationListCreated;
 
39
 
 
40
#if 0
 
41
static void dumpit (const AutomationList& al, string prefix = "")
 
42
{
 
43
        cerr << prefix << &al << endl;
 
44
        for (AutomationList::const_iterator i = al.begin(); i != al.end(); ++i) {
 
45
                cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
 
46
        }
 
47
        cerr << "\n";
 
48
}
 
49
#endif
 
50
AutomationList::AutomationList (Evoral::Parameter id)
 
51
        : ControlList(id)
 
52
{
 
53
        _state = Off;
 
54
        _style = Absolute;
 
55
        g_atomic_int_set (&_touching, 0);
 
56
 
 
57
        create_curve_if_necessary();
 
58
 
 
59
        assert(_parameter.type() != NullAutomation);
 
60
        AutomationListCreated(this);
 
61
}
 
62
 
 
63
AutomationList::AutomationList (const AutomationList& other)
 
64
        : StatefulDestructible()
 
65
        , ControlList(other)
 
66
{
 
67
        _style = other._style;
 
68
        _state = other._state;
 
69
        g_atomic_int_set (&_touching, other.touching());
 
70
 
 
71
        create_curve_if_necessary();
 
72
 
 
73
        assert(_parameter.type() != NullAutomation);
 
74
        AutomationListCreated(this);
 
75
}
 
76
 
 
77
AutomationList::AutomationList (const AutomationList& other, double start, double end)
 
78
        : ControlList(other, start, end)
 
79
{
 
80
        _style = other._style;
 
81
        _state = other._state;
 
82
        g_atomic_int_set (&_touching, other.touching());
 
83
 
 
84
        create_curve_if_necessary();
 
85
 
 
86
        assert(_parameter.type() != NullAutomation);
 
87
        AutomationListCreated(this);
 
88
}
 
89
 
 
90
/** @param id is used for legacy sessions where the type is not present
 
91
 * in or below the AutomationList node.  It is used if @param id is non-null.
 
92
 */
 
93
AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
 
94
        : ControlList(id)
 
95
{
 
96
        g_atomic_int_set (&_touching, 0);
 
97
        _state = Off;
 
98
        _style = Absolute;
 
99
 
 
100
        set_state (node, Stateful::loading_state_version);
 
101
 
 
102
        if (id) {
 
103
                _parameter = id;
 
104
        }
 
105
 
 
106
        create_curve_if_necessary();
 
107
 
 
108
        assert(_parameter.type() != NullAutomation);
 
109
        AutomationListCreated(this);
 
110
}
 
111
 
 
112
AutomationList::~AutomationList()
 
113
{
 
114
}
 
115
 
 
116
boost::shared_ptr<Evoral::ControlList>
 
117
AutomationList::create(Evoral::Parameter id)
 
118
{
 
119
        return boost::shared_ptr<Evoral::ControlList>(new AutomationList(id));
 
120
}
 
121
 
 
122
void
 
123
AutomationList::create_curve_if_necessary()
 
124
{
 
125
        switch (_parameter.type()) {
 
126
        case GainAutomation:
 
127
        case PanAzimuthAutomation:
 
128
        case PanElevationAutomation:
 
129
        case PanWidthAutomation:
 
130
        case FadeInAutomation:
 
131
        case FadeOutAutomation:
 
132
        case EnvelopeAutomation:
 
133
                create_curve();
 
134
                break;
 
135
        default:
 
136
                break;
 
137
        }
 
138
}
 
139
 
 
140
bool
 
141
AutomationList::operator== (const AutomationList& other)
 
142
{
 
143
        return _events == other._events;
 
144
}
 
145
 
 
146
AutomationList&
 
147
AutomationList::operator= (const AutomationList& other)
 
148
{
 
149
        if (this != &other) {
 
150
 
 
151
                _events.clear ();
 
152
 
 
153
                for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
 
154
                        _events.push_back (new Evoral::ControlEvent (**i));
 
155
                }
 
156
 
 
157
                _min_yval = other._min_yval;
 
158
                _max_yval = other._max_yval;
 
159
                _default_value = other._default_value;
 
160
 
 
161
                mark_dirty ();
 
162
                maybe_signal_changed ();
 
163
        }
 
164
 
 
165
        return *this;
 
166
}
 
167
 
 
168
void
 
169
AutomationList::maybe_signal_changed ()
 
170
{
 
171
        ControlList::maybe_signal_changed ();
 
172
 
 
173
        if (!ControlList::frozen()) {
 
174
                StateChanged (); /* EMIT SIGNAL */
 
175
        }
 
176
}
 
177
 
 
178
void
 
179
AutomationList::set_automation_state (AutoState s)
 
180
{
 
181
        if (s != _state) {
 
182
                _state = s;
 
183
                automation_state_changed (s); /* EMIT SIGNAL */
 
184
        }
 
185
}
 
186
 
 
187
void
 
188
AutomationList::set_automation_style (AutoStyle s)
 
189
{
 
190
        if (s != _style) {
 
191
                _style = s;
 
192
                automation_style_changed (); /* EMIT SIGNAL */
 
193
        }
 
194
}
 
195
 
 
196
void
 
197
AutomationList::start_touch (double when)
 
198
{
 
199
        if (_state == Touch) {
 
200
                start_write_pass (when);
 
201
        }
 
202
 
 
203
        g_atomic_int_set (&_touching, 1);
 
204
}
 
205
 
 
206
void
 
207
AutomationList::stop_touch (bool mark, double)
 
208
{
 
209
        if (g_atomic_int_get (&_touching) == 0) {
 
210
                /* this touch has already been stopped (probably by Automatable::transport_stopped),
 
211
                   so we've nothing to do.
 
212
                */
 
213
                return;
 
214
        }
 
215
 
 
216
        g_atomic_int_set (&_touching, 0);
 
217
 
 
218
        if (_state == Touch) {
 
219
 
 
220
                if (mark) {
 
221
                        
 
222
                        /* XXX need to mark the last added point with the
 
223
                         * current time 
 
224
                         */
 
225
                }
 
226
        }
 
227
}
 
228
 
 
229
void
 
230
AutomationList::thaw ()
 
231
{
 
232
        ControlList::thaw();
 
233
 
 
234
        if (_changed_when_thawed) {
 
235
                _changed_when_thawed = false;
 
236
                StateChanged(); /* EMIT SIGNAL */
 
237
        }
 
238
}
 
239
 
 
240
XMLNode&
 
241
AutomationList::get_state ()
 
242
{
 
243
        return state (true);
 
244
}
 
245
 
 
246
XMLNode&
 
247
AutomationList::state (bool full)
 
248
{
 
249
        XMLNode* root = new XMLNode (X_("AutomationList"));
 
250
        char buf[64];
 
251
        LocaleGuard lg (X_("POSIX"));
 
252
 
 
253
        root->add_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter));
 
254
 
 
255
        root->add_property ("id", id().to_s());
 
256
 
 
257
        snprintf (buf, sizeof (buf), "%.12g", _default_value);
 
258
        root->add_property ("default", buf);
 
259
        snprintf (buf, sizeof (buf), "%.12g", _min_yval);
 
260
        root->add_property ("min-yval", buf);
 
261
        snprintf (buf, sizeof (buf), "%.12g", _max_yval);
 
262
        root->add_property ("max-yval", buf);
 
263
 
 
264
        root->add_property ("interpolation-style", enum_2_string (_interpolation));
 
265
 
 
266
        if (full) {
 
267
                /* never serialize state with Write enabled - too dangerous
 
268
                   for the user's data
 
269
                */
 
270
                if (_state != Write) {
 
271
                        root->add_property ("state", auto_state_to_string (_state));
 
272
                } else {
 
273
                        root->add_property ("state", auto_state_to_string (Off));
 
274
                }
 
275
        } else {
 
276
                /* never save anything but Off for automation state to a template */
 
277
                root->add_property ("state", auto_state_to_string (Off));
 
278
        }
 
279
 
 
280
        root->add_property ("style", auto_style_to_string (_style));
 
281
 
 
282
        if (!_events.empty()) {
 
283
                root->add_child_nocopy (serialize_events());
 
284
        }
 
285
 
 
286
        return *root;
 
287
}
 
288
 
 
289
XMLNode&
 
290
AutomationList::serialize_events ()
 
291
{
 
292
        XMLNode* node = new XMLNode (X_("events"));
 
293
        stringstream str;
 
294
 
 
295
        str.precision(15);  //10 digits is enough digits for 24 hours at 96kHz
 
296
 
 
297
        for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
 
298
                str << (double) (*xx)->when;
 
299
                str << ' ';
 
300
                str <<(double) (*xx)->value;
 
301
                str << '\n';
 
302
        }
 
303
 
 
304
        /* XML is a bit wierd */
 
305
 
 
306
        XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
 
307
        content_node->set_content (str.str());
 
308
 
 
309
        node->add_child_nocopy (*content_node);
 
310
 
 
311
        return *node;
 
312
}
 
313
 
 
314
int
 
315
AutomationList::deserialize_events (const XMLNode& node)
 
316
{
 
317
        if (node.children().empty()) {
 
318
                return -1;
 
319
        }
 
320
 
 
321
        XMLNode* content_node = node.children().front();
 
322
 
 
323
        if (content_node->content().empty()) {
 
324
                return -1;
 
325
        }
 
326
 
 
327
        ControlList::freeze ();
 
328
        clear ();
 
329
 
 
330
        stringstream str (content_node->content());
 
331
 
 
332
        double x;
 
333
        double y;
 
334
        bool ok = true;
 
335
 
 
336
        while (str) {
 
337
                str >> x;
 
338
                if (!str) {
 
339
                        break;
 
340
                }
 
341
                str >> y;
 
342
                if (!str) {
 
343
                        ok = false;
 
344
                        break;
 
345
                }
 
346
                fast_simple_add (x, y);
 
347
        }
 
348
 
 
349
        thin ();
 
350
 
 
351
        if (!ok) {
 
352
                clear ();
 
353
                error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
 
354
        } else {
 
355
                mark_dirty ();
 
356
                maybe_signal_changed ();
 
357
        }
 
358
 
 
359
        thaw ();
 
360
 
 
361
        return 0;
 
362
}
 
363
 
 
364
int
 
365
AutomationList::set_state (const XMLNode& node, int version)
 
366
{
 
367
        LocaleGuard lg (X_("POSIX"));
 
368
        XMLNodeList nlist = node.children();
 
369
        XMLNode* nsos;
 
370
        XMLNodeIterator niter;
 
371
        const XMLProperty* prop;
 
372
 
 
373
        if (node.name() == X_("events")) {
 
374
                /* partial state setting*/
 
375
                return deserialize_events (node);
 
376
        }
 
377
 
 
378
        if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
 
379
 
 
380
                if ((nsos = node.child (X_("AutomationList")))) {
 
381
                        /* new school in old school clothing */
 
382
                        return set_state (*nsos, version);
 
383
                }
 
384
 
 
385
                /* old school */
 
386
 
 
387
                const XMLNodeList& elist = node.children();
 
388
                XMLNodeConstIterator i;
 
389
                XMLProperty* prop;
 
390
                pframes_t x;
 
391
                double y;
 
392
 
 
393
                ControlList::freeze ();
 
394
                clear ();
 
395
 
 
396
                for (i = elist.begin(); i != elist.end(); ++i) {
 
397
 
 
398
                        if ((prop = (*i)->property ("x")) == 0) {
 
399
                                error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
 
400
                                continue;
 
401
                        }
 
402
                        x = atoi (prop->value().c_str());
 
403
 
 
404
                        if ((prop = (*i)->property ("y")) == 0) {
 
405
                                error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
 
406
                                continue;
 
407
                        }
 
408
                        y = atof (prop->value().c_str());
 
409
 
 
410
                        fast_simple_add (x, y);
 
411
                }
 
412
 
 
413
                thin ();
 
414
 
 
415
                thaw ();
 
416
 
 
417
                return 0;
 
418
        }
 
419
 
 
420
        if (node.name() != X_("AutomationList") ) {
 
421
                error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
 
422
                return -1;
 
423
        }
 
424
 
 
425
        if (set_id (node)) {
 
426
                /* update session AL list */
 
427
                AutomationListCreated(this);
 
428
        }
 
429
 
 
430
        if ((prop = node.property (X_("automation-id"))) != 0){
 
431
                _parameter = EventTypeMap::instance().new_parameter(prop->value());
 
432
        } else {
 
433
                warning << "Legacy session: automation list has no automation-id property." << endmsg;
 
434
        }
 
435
 
 
436
        if ((prop = node.property (X_("interpolation-style"))) != 0) {
 
437
                _interpolation = (InterpolationStyle)string_2_enum(prop->value(), _interpolation);
 
438
        } else {
 
439
                _interpolation = Linear;
 
440
        }
 
441
 
 
442
        if ((prop = node.property (X_("default"))) != 0){
 
443
                _default_value = atof (prop->value().c_str());
 
444
        } else {
 
445
                _default_value = 0.0;
 
446
        }
 
447
 
 
448
        if ((prop = node.property (X_("style"))) != 0) {
 
449
                _style = string_to_auto_style (prop->value());
 
450
        } else {
 
451
                _style = Absolute;
 
452
        }
 
453
 
 
454
        if ((prop = node.property (X_("state"))) != 0) {
 
455
                _state = string_to_auto_state (prop->value());
 
456
                if (_state == Write) {
 
457
                        _state = Off;
 
458
                }
 
459
                automation_state_changed(_state);
 
460
        } else {
 
461
                _state = Off;
 
462
        }
 
463
 
 
464
        if ((prop = node.property (X_("min-yval"))) != 0) {
 
465
                _min_yval = atof (prop->value ().c_str());
 
466
        } else {
 
467
                _min_yval = FLT_MIN;
 
468
        }
 
469
 
 
470
        if ((prop = node.property (X_("max-yval"))) != 0) {
 
471
                _max_yval = atof (prop->value ().c_str());
 
472
        } else {
 
473
                _max_yval = FLT_MAX;
 
474
        }
 
475
 
 
476
        bool have_events = false;
 
477
 
 
478
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
479
                if ((*niter)->name() == X_("events")) {
 
480
                        deserialize_events (*(*niter));
 
481
                        have_events = true;
 
482
                }
 
483
        }
 
484
 
 
485
        if (!have_events) {
 
486
                /* there was no Events child node; clear any current events */
 
487
                freeze ();
 
488
                clear ();
 
489
                mark_dirty ();
 
490
                maybe_signal_changed ();
 
491
                thaw ();
 
492
        }
 
493
 
 
494
        return 0;
 
495
}
 
496
 
 
497
bool
 
498
AutomationList::operator!= (AutomationList const & other) const
 
499
{
 
500
        return (
 
501
                static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) ||
 
502
                _state != other._state ||
 
503
                _style != other._style ||
 
504
                _touching != other._touching
 
505
                );
 
506
}
 
507
 
 
508
PBD::PropertyBase *
 
509
AutomationListProperty::clone () const
 
510
{
 
511
        return new AutomationListProperty (
 
512
                this->property_id(),
 
513
                boost::shared_ptr<AutomationList> (new AutomationList (*this->_old.get())),
 
514
                boost::shared_ptr<AutomationList> (new AutomationList (*this->_current.get()))
 
515
                );
 
516
}
 
517