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