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