fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / audio_playlist.cc
1 /*
2     Copyright (C) 2003 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 <algorithm>
21
22 #include <cstdlib>
23
24 #include "ardour/types.h"
25 #include "ardour/debug.h"
26 #include "ardour/audioplaylist.h"
27 #include "ardour/audioregion.h"
28 #include "ardour/region_sorters.h"
29 #include "ardour/session.h"
30
31 #include "pbd/i18n.h"
32
33 using namespace ARDOUR;
34 using namespace std;
35 using namespace PBD;
36
37 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
38         : Playlist (session, node, DataType::AUDIO, hidden)
39 {
40 #ifndef NDEBUG
41         XMLProperty const * prop = node.property("type");
42         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
43 #endif
44
45         in_set_state++;
46         if (set_state (node, Stateful::loading_state_version)) {
47                 throw failed_constructor();
48         }
49         in_set_state--;
50
51         relayer ();
52
53         load_legacy_crossfades (node, Stateful::loading_state_version);
54 }
55
56 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
57         : Playlist (session, name, DataType::AUDIO, hidden)
58 {
59 }
60
61 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
62         : Playlist (other, name, hidden)
63 {
64 }
65
66 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
67         : Playlist (other, start, cnt, name, hidden)
68 {
69         RegionReadLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
70         in_set_state++;
71
72         framepos_t const end = start + cnt - 1;
73
74         /* Audio regions that have been created by the Playlist constructor
75            will currently have the same fade in/out as the regions that they
76            were created from.  This is wrong, so reset the fades here.
77         */
78
79         RegionList::iterator ours = regions.begin ();
80
81         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
82                 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
83                 assert (region);
84
85                 framecnt_t fade_in = 64;
86                 framecnt_t fade_out = 64;
87
88                 switch (region->coverage (start, end)) {
89                 case Evoral::OverlapNone:
90                         continue;
91
92                 case Evoral::OverlapInternal:
93                 {
94                         framecnt_t const offset = start - region->position ();
95                         framecnt_t const trim = region->last_frame() - end;
96                         if (region->fade_in()->back()->when > offset) {
97                                 fade_in = region->fade_in()->back()->when - offset;
98                         }
99                         if (region->fade_out()->back()->when > trim) {
100                                 fade_out = region->fade_out()->back()->when - trim;
101                         }
102                         break;
103                 }
104
105                 case Evoral::OverlapStart: {
106                         if (end > region->position() + region->fade_in()->back()->when)
107                                 fade_in = region->fade_in()->back()->when;  //end is after fade-in, preserve the fade-in
108                         if (end > region->last_frame() - region->fade_out()->back()->when)
109                                 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end );  //end is inside the fadeout, preserve the fades endpoint
110                         break;
111                 }
112
113                 case Evoral::OverlapEnd: {
114                         if (start < region->last_frame() - region->fade_out()->back()->when)  //start is before fade-out, preserve the fadeout
115                                 fade_out = region->fade_out()->back()->when;
116
117                         if (start < region->position() + region->fade_in()->back()->when)
118                                 fade_in = region->fade_in()->back()->when - (start - region->position());  //end is inside the fade-in, preserve the fade-in endpoint
119                         break;
120                 }
121
122                 case Evoral::OverlapExternal:
123                         fade_in = region->fade_in()->back()->when;
124                         fade_out = region->fade_out()->back()->when;
125                         break;
126                 }
127
128                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
129                 assert (our_region);
130
131                 our_region->set_fade_in_length (fade_in);
132                 our_region->set_fade_out_length (fade_out);
133                 ++ours;
134         }
135
136         in_set_state--;
137
138         /* this constructor does NOT notify others (session) */
139 }
140
141 /** Sort by descending layer and then by ascending position */
142 struct ReadSorter {
143     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
144             if (a->layer() != b->layer()) {
145                     return a->layer() > b->layer();
146             }
147
148             return a->position() < b->position();
149     }
150 };
151
152 /** A segment of region that needs to be read */
153 struct Segment {
154         Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
155
156         boost::shared_ptr<AudioRegion> region; ///< the region
157         Evoral::Range<framepos_t> range;       ///< range of the region to read, in session frames
158 };
159
160 /** @param start Start position in session frames.
161  *  @param cnt Number of frames to read.
162  */
163 ARDOUR::framecnt_t
164 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
165                      framecnt_t cnt, unsigned chan_n)
166 {
167         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 mixdown @ %6 gain @ %7\n",
168                                                            name(), start, cnt, chan_n, regions.size(), mixdown_buffer, gain_buffer));
169
170         /* optimizing this memset() away involves a lot of conditionals
171            that may well cause more of a hit due to cache misses
172            and related stuff than just doing this here.
173
174            it would be great if someone could measure this
175            at some point.
176
177            one way or another, parts of the requested area
178            that are not written to by Region::region_at()
179            for all Regions that cover the area need to be
180            zeroed.
181         */
182
183         memset (buf, 0, sizeof (Sample) * cnt);
184
185         /* this function is never called from a realtime thread, so
186            its OK to block (for short intervals).
187         */
188
189         Playlist::RegionReadLock rl (this);
190
191         /* Find all the regions that are involved in the bit we are reading,
192            and sort them by descending layer and ascending position.
193         */
194         boost::shared_ptr<RegionList> all = regions_touched_locked (start, start + cnt - 1);
195         all->sort (ReadSorter ());
196
197         /* This will be a list of the bits of our read range that we have
198            handled completely (ie for which no more regions need to be read).
199            It is a list of ranges in session frames.
200         */
201         Evoral::RangeList<framepos_t> done;
202
203         /* This will be a list of the bits of regions that we need to read */
204         list<Segment> to_do;
205
206         /* Now go through the `all' list filling in `to_do' and `done' */
207         for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
208                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
209
210                 /* muted regions don't figure into it at all */
211                 if ( ar->muted() )
212                         continue;
213
214                 /* Work out which bits of this region need to be read;
215                    first, trim to the range we are reading...
216                 */
217                 Evoral::Range<framepos_t> region_range = ar->range ();
218                 region_range.from = max (region_range.from, start);
219                 region_range.to = min (region_range.to, start + cnt - 1);
220
221                 /* ... and then remove the bits that are already done */
222
223                 Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
224
225                 /* Make a note to read those bits, adding their bodies (the parts between end-of-fade-in
226                    and start-of-fade-out) to the `done' list.
227                 */
228
229                 Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
230
231                 for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
232                         Evoral::Range<framepos_t> d = *j;
233                         to_do.push_back (Segment (ar, d));
234
235                         if (ar->opaque ()) {
236                                 /* Cut this range down to just the body and mark it done */
237                                 Evoral::Range<framepos_t> body = ar->body_range ();
238                                 if (body.from < d.to && body.to > d.from) {
239                                         d.from = max (d.from, body.from);
240                                         d.to = min (d.to, body.to);
241                                         done.add (d);
242                                 }
243                         }
244                 }
245         }
246
247         /* Now go backwards through the to_do list doing the actual reads */
248         for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
249                 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\tPlaylist %1 read %2 @ %3 for %4, channel %5, buf @ %6 offset %7\n",
250                                                                    name(), i->region->name(), i->range.from,
251                                                                    i->range.to - i->range.from + 1, (int) chan_n,
252                                                                    buf, i->range.from - start));
253                 i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
254         }
255
256         return cnt;
257 }
258
259 void
260 AudioPlaylist::dump () const
261 {
262         boost::shared_ptr<Region>r;
263
264         cerr << "Playlist \"" << _name << "\" " << endl
265              << regions.size() << " regions "
266              << endl;
267
268         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
269                 r = *i;
270                 cerr << "  " << r->name() << " @ " << r << " ["
271                      << r->start() << "+" << r->length()
272                      << "] at "
273                      << r->position()
274                      << " on layer "
275                      << r->layer ()
276                      << endl;
277         }
278 }
279
280 bool
281 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
282 {
283         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
284
285         if (!r) {
286                 return false;
287         }
288
289         bool changed = false;
290
291         {
292                 RegionWriteLock rlock (this);
293
294                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
295
296                         RegionList::iterator tmp = i;
297                         ++tmp;
298
299                         if ((*i) == region) {
300                                 regions.erase (i);
301                                 changed = true;
302                         }
303
304                         i = tmp;
305                 }
306
307                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
308
309                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
310                         ++xtmp;
311
312                         if ((*x) == region) {
313                                 all_regions.erase (x);
314                                 changed = true;
315                         }
316
317                         x = xtmp;
318                 }
319
320                 region->set_playlist (boost::shared_ptr<Playlist>());
321         }
322
323         if (changed) {
324                 /* overload this, it normally means "removed", not destroyed */
325                 notify_region_removed (region);
326         }
327
328         return changed;
329 }
330
331 bool
332 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
333 {
334         if (in_flush || in_set_state) {
335                 return false;
336         }
337
338         PropertyChange bounds;
339         bounds.add (Properties::start);
340         bounds.add (Properties::position);
341         bounds.add (Properties::length);
342
343         PropertyChange our_interests;
344
345         our_interests.add (Properties::fade_in_active);
346         our_interests.add (Properties::fade_out_active);
347         our_interests.add (Properties::scale_amplitude);
348         our_interests.add (Properties::envelope_active);
349         our_interests.add (Properties::envelope);
350         our_interests.add (Properties::fade_in);
351         our_interests.add (Properties::fade_out);
352
353         bool parent_wants_notify;
354
355         parent_wants_notify = Playlist::region_changed (what_changed, region);
356         /* if bounds changed, we have already done notify_contents_changed ()*/
357         if ((parent_wants_notify || what_changed.contains (our_interests)) && !what_changed.contains (bounds)) {
358                 notify_contents_changed ();
359         }
360
361         return true;
362 }
363
364 void
365 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
366 {
367         RegionSortByPosition cmp;
368         boost::shared_ptr<AudioRegion> ar;
369
370         sort (copies.begin(), copies.end(), cmp);
371
372         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
373
374         /* disable fade in of the first region */
375
376         if (ar) {
377                 ar->set_fade_in_active (false);
378         }
379
380         ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
381
382         /* disable fade out of the last region */
383
384         if (ar) {
385                 ar->set_fade_out_active (false);
386         }
387 }
388
389 void
390 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
391 {
392         RegionSortByPosition cmp;
393         boost::shared_ptr<AudioRegion> ar;
394         boost::shared_ptr<AudioRegion> cr;
395
396         if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
397                 return;
398         }
399
400         sort (originals.begin(), originals.end(), cmp);
401
402         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
403
404         /* copy the fade in of the first into the compound region */
405
406         if (ar) {
407                 cr->set_fade_in (ar->fade_in());
408         }
409
410         ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
411
412         if (ar) {
413                 /* copy the fade out of the last into the compound region */
414                 cr->set_fade_out (ar->fade_out());
415         }
416 }
417
418 void
419 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
420 {
421         RegionSortByPosition cmp;
422         boost::shared_ptr<AudioRegion> ar;
423         boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
424
425         if (!cr) {
426                 return;
427         }
428
429         sort (originals.begin(), originals.end(), cmp);
430
431         /* no need to call clear_changes() on the originals because that is
432          * done within Playlist::uncombine ()
433          */
434
435         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
436
437                 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
438                         continue;
439                 }
440
441                 /* scale the uncombined regions by any gain setting for the
442                  * compound one.
443                  */
444
445                 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
446
447                 if (i == originals.begin()) {
448
449                         /* copy the compound region's fade in back into the first
450                            original region.
451                         */
452
453                         if (cr->fade_in()->back()->when <= ar->length()) {
454                                 /* don't do this if the fade is longer than the
455                                  * region
456                                  */
457                                 ar->set_fade_in (cr->fade_in());
458                         }
459
460
461                 } else if (*i == originals.back()) {
462
463                         /* copy the compound region's fade out back into the last
464                            original region.
465                         */
466
467                         if (cr->fade_out()->back()->when <= ar->length()) {
468                                 /* don't do this if the fade is longer than the
469                                  * region
470                                  */
471                                 ar->set_fade_out (cr->fade_out());
472                         }
473
474                 }
475
476                 _session.add_command (new StatefulDiffCommand (*i));
477         }
478 }
479
480 int
481 AudioPlaylist::set_state (const XMLNode& node, int version)
482 {
483         return Playlist::set_state (node, version);
484 }
485
486 void
487 AudioPlaylist::load_legacy_crossfades (const XMLNode& node, int version)
488 {
489         /* Read legacy Crossfade nodes and set up region fades accordingly */
490
491         XMLNodeList children = node.children ();
492         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
493                 if ((*i)->name() == X_("Crossfade")) {
494
495                         XMLProperty const * p = (*i)->property (X_("active"));
496                         assert (p);
497
498                         if (!string_is_affirmative (p->value())) {
499                                 continue;
500                         }
501
502                         if ((p = (*i)->property (X_("in"))) == 0) {
503                                 continue;
504                         }
505
506                         boost::shared_ptr<Region> in = region_by_id (PBD::ID (p->value ()));
507
508                         if (!in) {
509                                 warning << string_compose (_("Legacy crossfade involved an incoming region not present in playlist \"%1\" - crossfade discarded"),
510                                                            name())
511                                         << endmsg;
512                                 continue;
513                         }
514
515                         boost::shared_ptr<AudioRegion> in_a = boost::dynamic_pointer_cast<AudioRegion> (in);
516                         assert (in_a);
517
518                         if ((p = (*i)->property (X_("out"))) == 0) {
519                                 continue;
520                         }
521
522                         boost::shared_ptr<Region> out = region_by_id (PBD::ID (p->value ()));
523
524                         if (!out) {
525                                 warning << string_compose (_("Legacy crossfade involved an outgoing region not present in playlist \"%1\" - crossfade discarded"),
526                                                            name())
527                                         << endmsg;
528                                 continue;
529                         }
530
531                         boost::shared_ptr<AudioRegion> out_a = boost::dynamic_pointer_cast<AudioRegion> (out);
532                         assert (out_a);
533
534                         /* now decide whether to add a fade in or fade out
535                          * xfade and to which region
536                          */
537
538                         if (in->layer() <= out->layer()) {
539
540                                 /* incoming region is below the outgoing one,
541                                  * so apply a fade out to the outgoing one
542                                  */
543
544                                 const XMLNodeList c = (*i)->children ();
545
546                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
547                                         if ((*j)->name() == X_("FadeOut")) {
548                                                 out_a->fade_out()->set_state (**j, version);
549                                         } else if ((*j)->name() == X_("FadeIn")) {
550                                                 out_a->inverse_fade_out()->set_state (**j, version);
551                                         }
552                                 }
553
554                                 out_a->set_fade_out_active (true);
555
556                         } else {
557
558                                 /* apply a fade in to the incoming region,
559                                  * since its above the outgoing one
560                                  */
561
562                                 const XMLNodeList c = (*i)->children ();
563
564                                 for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) {
565                                         if ((*j)->name() == X_("FadeIn")) {
566                                                 in_a->fade_in()->set_state (**j, version);
567                                         } else if ((*j)->name() == X_("FadeOut")) {
568                                                 in_a->inverse_fade_in()->set_state (**j, version);
569                                         }
570                                 }
571
572                                 in_a->set_fade_in_active (true);
573                         }
574                 }
575         }
576 }