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