fix thinko in playlist constructor for cut and copy
[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/configuration.h"
27 #include "ardour/audioplaylist.h"
28 #include "ardour/audioregion.h"
29 #include "ardour/crossfade.h"
30 #include "ardour/session.h"
31 #include "pbd/enumwriter.h"
32
33 #include "i18n.h"
34
35 using namespace ARDOUR;
36 using namespace std;
37 using namespace PBD;
38
39 namespace ARDOUR {
40         namespace Properties {
41                 PBD::PropertyDescriptor<bool> crossfades;
42         }
43 }
44
45 void
46 AudioPlaylist::make_property_quarks ()
47 {
48         Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
49         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
50 }
51
52 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
53         : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
54         , _playlist (pl)
55 {
56         
57 }
58
59 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
60         : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
61         , _playlist (p._playlist)
62 {
63
64 }
65
66
67 CrossfadeListProperty *
68 CrossfadeListProperty::create () const
69 {
70         return new CrossfadeListProperty (_playlist);
71 }
72
73 CrossfadeListProperty *
74 CrossfadeListProperty::clone () const
75 {
76         return new CrossfadeListProperty (*this);
77 }
78
79 void
80 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
81 {
82         /* Crossfades are not written to any state when they are no
83            longer in use, so we must write their state here.
84         */
85
86         XMLNode& c = xfade->get_state ();
87         node.add_child_nocopy (c);
88 }
89
90 boost::shared_ptr<Crossfade>
91 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
92 {
93         XMLNodeList const c = node.children ();
94         assert (c.size() == 1);
95         return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
96 }
97
98
99 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
100         : Playlist (session, node, DataType::AUDIO, hidden)
101         , _crossfades (*this)
102 {
103 #ifndef NDEBUG
104         const XMLProperty* prop = node.property("type");
105         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
106 #endif
107
108         add_property (_crossfades);
109
110         in_set_state++;
111         set_state (node, Stateful::loading_state_version);
112         in_set_state--;
113 }
114
115 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
116         : Playlist (session, name, DataType::AUDIO, hidden)
117         , _crossfades (*this)
118 {
119         add_property (_crossfades);
120 }
121
122 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
123         : Playlist (other, name, hidden)
124         , _crossfades (*this)
125 {
126         add_property (_crossfades);
127         
128         RegionList::const_iterator in_o  = other->regions.begin();
129         RegionList::iterator in_n = regions.begin();
130
131         while (in_o != other->regions.end()) {
132                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
133
134                 // We look only for crossfades which begin with the current region, so we don't get doubles
135                 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
136                         if ((*xfades)->in() == ar) {
137                                 // We found one! Now copy it!
138
139                                 RegionList::const_iterator out_o = other->regions.begin();
140                                 RegionList::const_iterator out_n = regions.begin();
141
142                                 while (out_o != other->regions.end()) {
143
144                                         boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
145
146                                         if ((*xfades)->out() == ar2) {
147                                                 boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
148                                                 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
149                                                 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
150                                                 add_crossfade(new_fade);
151                                                 break;
152                                         }
153
154                                         out_o++;
155                                         out_n++;
156                                 }
157 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
158                         }
159                 }
160
161                 in_o++;
162                 in_n++;
163         }
164 }
165
166 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
167         : Playlist (other, start, cnt, name, hidden)
168         , _crossfades (*this)
169 {
170         RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
171         in_set_state++;
172         
173         add_property (_crossfades);
174
175         framepos_t const end = start + cnt - 1;
176
177         /* Audio regions that have been created by the Playlist constructor
178            will currently have the same fade in/out as the regions that they
179            were created from.  This is wrong, so reset the fades here.
180         */
181
182         RegionList::iterator ours = regions.begin ();
183
184         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
185                 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
186                 assert (region);
187
188                 framecnt_t fade_in = 64;
189                 framecnt_t fade_out = 64;
190
191                 switch (region->coverage (start, end)) {
192                 case OverlapNone:
193                         continue;
194
195                 case OverlapInternal:
196                 {
197                         framecnt_t const offset = start - region->position ();
198                         framecnt_t const trim = region->last_frame() - end;
199                         if (region->fade_in()->back()->when > offset) {
200                                 fade_in = region->fade_in()->back()->when - offset;
201                         }
202                         if (region->fade_out()->back()->when > trim) {
203                                 fade_out = region->fade_out()->back()->when - trim;
204                         }
205                         break;
206                 }
207
208                 case OverlapStart: {
209                         position = region->position() - start;
210                         len = end - region->position();
211
212                         if (end > region->position() + region->fade_in().back()->when)
213                                 fade_in_len = region->fade_in().back()->when;  //end is after fade-in, preserve the fade-in
214                         if (end > region->last_frame() - region->fade_out().back()->when)
215                                 fade_out_len = region->fade_out().back()->when - ( region->last_frame() - end );  //end is inside the fadeout, preserve the fades endpoint
216                         break;
217                 }
218                 
219                 case OverlapEnd: {
220                         position = 0;
221                         offset = start - region->position();
222                         len = region->length() - offset;
223
224                         if (start < region->last_frame() - region->fade_out().back()->when)  //start is before fade-out, preserve the fadeout
225                                 fade_out_len = region->fade_out().back()->when;
226                         
227                         if (start < region->position() + region->fade_in().back()->when)
228                                 fade_in_len = region->fade_in().back()->when - (start - region->position());  //end is inside the fade-in, preserve the fade-in endpoint
229                         break;
230                 }
231                 
232                 case OverlapExternal:
233                         fade_in = region->fade_in()->back()->when;
234                         fade_out = region->fade_out()->back()->when;
235                         break;
236                 }
237
238                 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
239                 assert (our_region);
240
241                 our_region->set_fade_in_length (fade_in);
242                 our_region->set_fade_out_length (fade_out);
243                 ++ours;
244         }
245
246         in_set_state--;
247
248         /* this constructor does NOT notify others (session) */
249 }
250
251 AudioPlaylist::~AudioPlaylist ()
252 {
253         _crossfades.clear ();
254 }
255
256 struct RegionSortByLayer {
257     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
258             return a->layer() < b->layer();
259     }
260 };
261
262 ARDOUR::framecnt_t
263 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
264                      framecnt_t cnt, unsigned chan_n)
265 {
266         framecnt_t ret = cnt;
267
268         /* optimizing this memset() away involves a lot of conditionals
269            that may well cause more of a hit due to cache misses
270            and related stuff than just doing this here.
271
272            it would be great if someone could measure this
273            at some point.
274
275            one way or another, parts of the requested area
276            that are not written to by Region::region_at()
277            for all Regions that cover the area need to be
278            zeroed.
279         */
280
281         memset (buf, 0, sizeof (Sample) * cnt);
282
283         /* this function is never called from a realtime thread, so
284            its OK to block (for short intervals).
285         */
286
287         Glib::RecMutex::Lock rm (region_lock);
288
289         framepos_t const end = start + cnt - 1;
290         framecnt_t read_frames = 0;
291         framecnt_t skip_frames = 0;
292         _read_data_count = 0;
293
294         _read_data_count = 0;
295
296         RegionList* rlist = regions_to_read (start, start+cnt);
297
298         if (rlist->empty()) {
299                 delete rlist;
300                 return cnt;
301         }
302
303         map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
304         map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
305         vector<uint32_t> relevant_layers;
306
307         for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
308                 if ((*i)->coverage (start, end) != OverlapNone) {
309                         relevant_regions[(*i)->layer()].push_back (*i);
310                         relevant_layers.push_back ((*i)->layer());
311                 }
312         }
313
314         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
315                 if ((*i)->coverage (start, end) != OverlapNone) {
316                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
317                 }
318         }
319
320 //      RegionSortByLayer layer_cmp;
321 //      relevant_regions.sort (layer_cmp);
322
323         /* XXX this whole per-layer approach is a hack that
324            should be removed once Crossfades become
325            CrossfadeRegions and we just grab a list of relevant
326            regions and call read_at() on all of them.
327         */
328
329         sort (relevant_layers.begin(), relevant_layers.end());
330
331         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
332
333                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
334                 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
335
336
337                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
338                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
339                         DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
340                         assert(ar);
341                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
342                         _read_data_count += ar->read_data_count();
343                 }
344
345                 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
346                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
347
348                         /* don't JACK up _read_data_count, since its the same data as we just
349                            read from the regions, and the OS should handle that for us.
350                         */
351                 }
352         }
353
354         delete rlist;
355         return ret;
356 }
357
358
359 void
360 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
361 {
362         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
363
364         if (in_set_state) {
365                 return;
366         }
367
368         if (r == 0) {
369                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
370                       << endmsg;
371                 return;
372         }
373
374         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
375
376                 if ((*i)->involves (r)) {
377                         i = _crossfades.erase (i);
378                 } else {
379                         ++i;
380                 }
381         }
382 }
383
384
385 void
386 AudioPlaylist::flush_notifications (bool from_undo)
387 {
388         Playlist::flush_notifications (from_undo);
389
390         if (in_flush) {
391                 return;
392         }
393
394         in_flush = true;
395
396         Crossfades::iterator a;
397         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
398                 NewCrossfade (*a); /* EMIT SIGNAL */
399         }
400
401         _pending_xfade_adds.clear ();
402
403         in_flush = false;
404 }
405
406 void
407 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
408 {
409         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
410         set<boost::shared_ptr<Crossfade> > updated;
411
412         if (ar == 0) {
413                 return;
414         }
415
416         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
417
418                 Crossfades::iterator tmp;
419
420                 tmp = x;
421                 ++tmp;
422
423                 /* only update them once */
424
425                 if ((*x)->involves (ar)) {
426
427                         pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
428
429                         if (u.second) {
430                                 /* x was successfully inserted into the set, so it has not already been updated */
431                                 try {
432                                         (*x)->refresh ();
433                                 }
434
435                                 catch (Crossfade::NoCrossfadeHere& err) {
436                                         // relax, Invalidated during refresh
437                                 }
438                         }
439                 }
440
441                 x = tmp;
442         }
443 }
444
445 void
446 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
447 {
448         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
449         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
450         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
451
452         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
453                 Crossfades::iterator tmp;
454                 tmp = x;
455                 ++tmp;
456
457                 boost::shared_ptr<Crossfade> fade;
458
459                 if ((*x)->_in == orig) {
460                         if (! (*x)->covers(right->position())) {
461                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
462                         } else {
463                                 // Overlap, the crossfade is copied on the left side of the right region instead
464                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
465                         }
466                 }
467
468                 if ((*x)->_out == orig) {
469                         if (! (*x)->covers(right->position())) {
470                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
471                         } else {
472                                 // Overlap, the crossfade is copied on the right side of the left region instead
473                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
474                         }
475                 }
476
477                 if (fade) {
478                         _crossfades.remove (*x);
479                         add_crossfade (fade);
480                 }
481                 x = tmp;
482         }
483 }
484
485 void
486 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
487 {
488         boost::shared_ptr<AudioRegion> other;
489         boost::shared_ptr<AudioRegion> region;
490         boost::shared_ptr<AudioRegion> top;
491         boost::shared_ptr<AudioRegion> bottom;
492         boost::shared_ptr<Crossfade>   xfade;
493         RegionList*  touched_regions = 0;
494
495         if (in_set_state || in_partition) {
496                 return;
497         }
498
499         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
500                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
501                       << endmsg;
502                 return;
503         }
504
505         if (!norefresh) {
506                 refresh_dependents (r);
507         }
508
509
510         if (!_session.config.get_auto_xfade()) {
511                 return;
512         }
513
514         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
515                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
516
517                 if (other == region) {
518                         continue;
519                 }
520
521                 if (other->muted() || region->muted()) {
522                         continue;
523                 }
524
525                 if (other->position() == r->position() && other->length() == r->length()) {
526                         /* precise overlay of two regions - no xfade */
527                         continue;
528                 }
529
530                 if (other->layer() < region->layer()) {
531                         top = region;
532                         bottom = other;
533                 } else {
534                         top = other;
535                         bottom = region;
536                 }
537
538                 if (!top->opaque()) {
539                         continue;
540                 }
541
542                 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
543
544                 delete touched_regions;
545                 touched_regions = 0;
546
547                 try {
548                         framecnt_t xfade_length;
549                         switch (c) {
550                         case OverlapNone:
551                                 break;
552
553                         case OverlapInternal:
554                                  /* {=============== top  =============}
555                                   *     [ ----- bottom  ------- ]
556                                   */
557                                 break;
558
559                         case OverlapExternal:
560
561                                 /*     [ -------- top ------- ]
562                                  * {=========== bottom =============}
563                                  */
564
565                                 /* to avoid discontinuities at the region boundaries of an internal
566                                    overlap (this region is completely within another), we create
567                                    two hidden crossfades at each boundary. this is not dependent
568                                    on the auto-xfade option, because we require it as basic
569                                    audio engineering.
570                                 */
571
572                                 xfade_length = min ((framecnt_t) 720, top->length());
573
574                                 if (top_region_at (top->first_frame()) == top) {
575
576                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
577                                         add_crossfade (xfade);
578                                 }
579
580                                 if (top_region_at (top->last_frame() - 1) == top) {
581
582                                         /*
583                                            only add a fade out if there is no region on top of the end of 'top' (which
584                                            would cover it).
585                                         */
586
587                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
588                                         add_crossfade (xfade);
589                                 }
590                                 break;
591                         case OverlapStart:
592
593                                 /*                   { ==== top ============ }
594                                  *   [---- bottom -------------------]
595                                  */
596
597                                 if (_session.config.get_xfade_model() == FullCrossfade) {
598                                         touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
599                                         if (touched_regions->size() <= 2) {
600                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
601                                                 add_crossfade (xfade);
602                                         }
603                                 } else {
604
605                                         touched_regions = regions_touched (top->first_frame(),
606                                                                            top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
607                                                                                                      top->length()));
608                                         if (touched_regions->size() <= 2) {
609                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
610                                                 add_crossfade (xfade);
611                                         }
612                                 }
613                                 break;
614                         case OverlapEnd:
615
616
617                                 /* [---- top ------------------------]
618                                  *                { ==== bottom ============ }
619                                  */
620
621                                 if (_session.config.get_xfade_model() == FullCrossfade) {
622
623                                         touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
624                                         if (touched_regions->size() <= 2) {
625                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
626                                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
627                                                 add_crossfade (xfade);
628                                         }
629
630                                 } else {
631                                         touched_regions = regions_touched (bottom->first_frame(),
632                                                                            bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
633                                                                                                         bottom->length()));
634                                         if (touched_regions->size() <= 2) {
635                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
636                                                 add_crossfade (xfade);
637                                         }
638                                 }
639                                 break;
640                         default:
641                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
642                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
643                                 add_crossfade (xfade);
644                         }
645                 }
646
647                 catch (failed_constructor& err) {
648                         continue;
649                 }
650
651                 catch (Crossfade::NoCrossfadeHere& err) {
652                         continue;
653                 }
654
655         }
656
657         delete touched_regions;
658 }
659
660 void
661 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
662 {
663         Crossfades::iterator ci;
664
665         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
666                 if (*(*ci) == *xfade) { // Crossfade::operator==()
667                         break;
668                 }
669         }
670
671         if (ci != _crossfades.end()) {
672                 // it will just go away
673         } else {
674                 _crossfades.push_back (xfade);
675
676                 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
677                 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
678
679                 notify_crossfade_added (xfade);
680         }
681 }
682
683 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
684 {
685         if (g_atomic_int_get(&block_notifications)) {
686                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
687         } else {
688                 NewCrossfade (x); /* EMIT SIGNAL */
689         }
690 }
691
692 void
693 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
694 {
695         Crossfades::iterator i;
696         boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
697
698         xfade->in()->resume_fade_in ();
699         xfade->out()->resume_fade_out ();
700
701         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
702                 _crossfades.erase (i);
703         }
704 }
705
706 int
707 AudioPlaylist::set_state (const XMLNode& node, int version)
708 {
709         XMLNode *child;
710         XMLNodeList nlist;
711         XMLNodeConstIterator niter;
712
713         in_set_state++;
714
715         Playlist::set_state (node, version);
716
717         freeze ();
718
719         nlist = node.children();
720
721         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
722
723                 child = *niter;
724
725                 if (child->name() != "Crossfade") {
726                         continue;
727                 }
728
729                 try {
730                         boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
731                         _crossfades.push_back (xfade);
732                         xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
733                         xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
734                         NewCrossfade(xfade);
735                 }
736
737                 catch (failed_constructor& err) {
738                         //      cout << string_compose (_("could not create crossfade object in playlist %1"),
739                         //        _name)
740                         //    << endl;
741                         continue;
742                 }
743         }
744
745         thaw ();
746         in_set_state--;
747
748         return 0;
749 }
750
751 void
752 AudioPlaylist::clear (bool with_signals)
753 {
754         _crossfades.clear ();
755         Playlist::clear (with_signals);
756 }
757
758 XMLNode&
759 AudioPlaylist::state (bool full_state)
760 {
761         XMLNode& node = Playlist::state (full_state);
762
763         if (full_state) {
764                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
765                         node.add_child_nocopy ((*i)->get_state());
766                 }
767         }
768
769         return node;
770 }
771
772 void
773 AudioPlaylist::dump () const
774 {
775         boost::shared_ptr<Region>r;
776         boost::shared_ptr<Crossfade> x;
777
778         cerr << "Playlist \"" << _name << "\" " << endl
779              << regions.size() << " regions "
780              << _crossfades.size() << " crossfades"
781              << endl;
782
783         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
784                 r = *i;
785                 cerr << "  " << r->name() << " @ " << r << " ["
786                      << r->start() << "+" << r->length()
787                      << "] at "
788                      << r->position()
789                      << " on layer "
790                      << r->layer ()
791                      << endl;
792         }
793
794         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
795                 x = *i;
796                 cerr << "  xfade ["
797                      << x->out()->name()
798                      << ','
799                      << x->in()->name()
800                      << " @ "
801                      << x->position()
802                      << " length = "
803                      << x->length ()
804                      << " active ? "
805                      << (x->active() ? "yes" : "no")
806                      << endl;
807         }
808 }
809
810 bool
811 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
812 {
813         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
814
815         if (!r) {
816                 return false;
817         }
818
819         bool changed = false;
820         Crossfades::iterator c, ctmp;
821         set<boost::shared_ptr<Crossfade> > unique_xfades;
822
823         {
824                 RegionLock rlock (this);
825
826                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
827
828                         RegionList::iterator tmp = i;
829                         ++tmp;
830
831                         if ((*i) == region) {
832                                 regions.erase (i);
833                                 changed = true;
834                         }
835
836                         i = tmp;
837                 }
838
839                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
840
841                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
842                         ++xtmp;
843
844                         if ((*x) == region) {
845                                 all_regions.erase (x);
846                                 changed = true;
847                         }
848
849                         x = xtmp;
850                 }
851
852                 region->set_playlist (boost::shared_ptr<Playlist>());
853         }
854
855         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
856                 ctmp = c;
857                 ++ctmp;
858
859                 if ((*c)->involves (r)) {
860                         unique_xfades.insert (*c);
861                         _crossfades.erase (c);
862                 }
863
864                 c = ctmp;
865         }
866
867         if (changed) {
868                 /* overload this, it normally means "removed", not destroyed */
869                 notify_region_removed (region);
870         }
871
872         return changed;
873 }
874
875 void
876 AudioPlaylist::crossfade_changed (const PropertyChange&)
877 {
878         if (in_flush || in_set_state) {
879                 return;
880         }
881
882         /* XXX is there a loop here? can an xfade change not happen
883            due to a playlist change? well, sure activation would
884            be an example. maybe we should check the type of change
885            that occured.
886         */
887
888         notify_contents_changed ();
889 }
890
891 bool
892 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
893 {
894         if (in_flush || in_set_state) {
895                 return false;
896         }
897
898         PropertyChange our_interests;
899
900         our_interests.add (Properties::fade_in_active);
901         our_interests.add (Properties::fade_out_active);
902         our_interests.add (Properties::scale_amplitude);
903         our_interests.add (Properties::envelope_active);
904         our_interests.add (Properties::envelope);
905         our_interests.add (Properties::fade_in);
906         our_interests.add (Properties::fade_out);
907
908         bool parent_wants_notify;
909
910         parent_wants_notify = Playlist::region_changed (what_changed, region);
911
912         if (parent_wants_notify || (what_changed.contains (our_interests))) {
913                 notify_contents_changed ();
914         }
915
916         return true;
917 }
918
919 void
920 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
921 {
922         RegionLock rlock (this);
923
924         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
925                 framepos_t const start = (*i)->position ();
926                 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
927
928                 if (frame >= start && frame <= end) {
929                         clist.push_back (*i);
930                 }
931         }
932 }
933
934 void
935 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
936 {
937         RegionLock rl (this, false);
938         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
939                 s (*i);
940         }
941 }
942
943 void
944 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
945 {
946         for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
947                 add_crossfade (*i);
948         }
949
950         /* don't remove crossfades here; they will be dealt with by the dependency code */
951 }
952
953 boost::shared_ptr<Crossfade>
954 AudioPlaylist::find_crossfade (const PBD::ID& id) const
955 {
956         Crossfades::const_iterator i = _crossfades.begin ();
957         while (i != _crossfades.end() && (*i)->id() != id) {
958                 ++i;
959         }
960
961         if (i == _crossfades.end()) {
962                 return boost::shared_ptr<Crossfade> ();
963         }
964
965         return *i;
966 }