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