tracing and small fixes to improve object destruction pathways
[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
406                 try {
407                         switch (c) {
408                         case OverlapNone:
409                                 break;
410
411                         case OverlapInternal:
412                                  /* {=============== top  =============}
413                                   *     [ ----- bottom  ------- ]
414                                   */
415                                 break;
416
417                         case OverlapExternal:
418
419                                 /*     [ -------- top ------- ]
420                                  * {=========== bottom =============}
421                                  */
422
423                                 /* to avoid discontinuities at the region boundaries of an internal
424                                    overlap (this region is completely within another), we create
425                                    two hidden crossfades at each boundary. this is not dependent
426                                    on the auto-xfade option, because we require it as basic
427                                    audio engineering.
428                                 */
429
430                                 xfade_length = min ((nframes_t) 720, top->length());
431
432                                 if (top_region_at (top->first_frame()) == top) {
433
434                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
435                                         add_crossfade (xfade);
436                                 }
437
438                                 if (top_region_at (top->last_frame() - 1) == top) {
439
440                                         /*
441                                            only add a fade out if there is no region on top of the end of 'top' (which
442                                            would cover it).
443                                         */
444
445                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
446                                         add_crossfade (xfade);
447                                 }
448                                 break;
449                         case OverlapStart:
450
451                                 /*                   { ==== top ============ }
452                                  *   [---- bottom -------------------]
453                                  */
454
455                                 if (_session.config.get_xfade_model() == FullCrossfade) {
456                                         touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
457                                         if (touched_regions->size() <= 2) {
458                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
459                                                 add_crossfade (xfade);
460                                         }
461                                 } else {
462
463                                         touched_regions = regions_touched (top->first_frame(),
464                                                                            top->first_frame() + min ((nframes_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
465                                                                                                      top->length()));
466                                         if (touched_regions->size() <= 2) {
467                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
468                                                 add_crossfade (xfade);
469                                         }
470                                 }
471                                 break;
472                         case OverlapEnd:
473
474
475                                 /* [---- top ------------------------]
476                                  *                { ==== bottom ============ }
477                                  */
478
479                                 if (_session.config.get_xfade_model() == FullCrossfade) {
480
481                                         touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
482                                         if (touched_regions->size() <= 2) {
483                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
484                                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
485                                                 add_crossfade (xfade);
486                                         }
487
488                                 } else {
489                                         touched_regions = regions_touched (bottom->first_frame(),
490                                                                            bottom->first_frame() + min ((nframes_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
491                                                                                                         bottom->length()));
492                                         if (touched_regions->size() <= 2) {
493                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
494                                                 add_crossfade (xfade);
495                                         }
496                                 }
497                                 break;
498                         default:
499                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
500                                                                                      _session.config.get_xfade_model(), _session.config.get_xfades_active()));
501                                 add_crossfade (xfade);
502                         }
503                 }
504
505                 catch (failed_constructor& err) {
506                         continue;
507                 }
508
509                 catch (Crossfade::NoCrossfadeHere& err) {
510                         continue;
511                 }
512
513         }
514
515         delete touched_regions;
516 }
517
518 void
519 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
520 {
521         Crossfades::iterator ci;
522
523         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
524                 if (*(*ci) == *xfade) { // Crossfade::operator==()
525                         break;
526                 }
527         }
528
529         if (ci != _crossfades.end()) {
530                 // it will just go away
531         } else {
532                 _crossfades.push_back (xfade);
533
534                 xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
535                 xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
536
537                 notify_crossfade_added (xfade);
538         }
539 }
540
541 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
542 {
543         if (g_atomic_int_get(&block_notifications)) {
544                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
545         } else {
546                 NewCrossfade (x); /* EMIT SIGNAL */
547         }
548 }
549
550 void
551 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
552 {
553         Crossfades::iterator i;
554         boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
555
556         xfade->in()->resume_fade_in ();
557         xfade->out()->resume_fade_out ();
558
559         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
560                 _crossfades.erase (i);
561         }
562 }
563
564 int
565 AudioPlaylist::set_state (const XMLNode& node, int version)
566 {
567         XMLNode *child;
568         XMLNodeList nlist;
569         XMLNodeConstIterator niter;
570
571         in_set_state++;
572
573         Playlist::set_state (node, version);
574
575         freeze ();
576
577         nlist = node.children();
578
579         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
580
581                 child = *niter;
582
583                 if (child->name() != "Crossfade") {
584                         continue;
585                 }
586
587                 try {
588                         boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
589                         _crossfades.push_back (xfade);
590                         xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
591                         xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
592                         NewCrossfade(xfade);
593                 }
594
595                 catch (failed_constructor& err) {
596                         //      cout << string_compose (_("could not create crossfade object in playlist %1"),
597                         //        _name)
598                         //    << endl;
599                         continue;
600                 }
601         }
602
603         thaw ();
604         in_set_state--;
605
606         return 0;
607 }
608
609 void
610 AudioPlaylist::clear (bool with_signals)
611 {
612         _crossfades.clear ();
613         Playlist::clear (with_signals);
614 }
615
616 XMLNode&
617 AudioPlaylist::state (bool full_state)
618 {
619         XMLNode& node = Playlist::state (full_state);
620
621         if (full_state) {
622                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
623                         node.add_child_nocopy ((*i)->get_state());
624                 }
625         }
626
627         return node;
628 }
629
630 void
631 AudioPlaylist::dump () const
632 {
633         boost::shared_ptr<Region>r;
634         boost::shared_ptr<Crossfade> x;
635
636         cerr << "Playlist \"" << _name << "\" " << endl
637              << regions.size() << " regions "
638              << _crossfades.size() << " crossfades"
639              << endl;
640
641         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
642                 r = *i;
643                 cerr << "  " << r->name() << " @ " << r << " ["
644                      << r->start() << "+" << r->length()
645                      << "] at "
646                      << r->position()
647                      << " on layer "
648                      << r->layer ()
649                      << endl;
650         }
651
652         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
653                 x = *i;
654                 cerr << "  xfade ["
655                      << x->out()->name()
656                      << ','
657                      << x->in()->name()
658                      << " @ "
659                      << x->position()
660                      << " length = "
661                      << x->length ()
662                      << " active ? "
663                      << (x->active() ? "yes" : "no")
664                      << endl;
665         }
666 }
667
668 bool
669 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
670 {
671         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
672         bool changed = false;
673         Crossfades::iterator c, ctmp;
674         set<boost::shared_ptr<Crossfade> > unique_xfades;
675
676         if (r == 0) {
677                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
678                       << endmsg;
679                 /*NOTREACHED*/
680                 return false;
681         }
682
683         {
684                 RegionLock rlock (this);
685
686                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
687
688                         RegionList::iterator tmp = i;
689                         ++tmp;
690
691                         if ((*i) == region) {
692                                 regions.erase (i);
693                                 changed = true;
694                         }
695
696                         i = tmp;
697                 }
698
699                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
700
701                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
702                         ++xtmp;
703
704                         if ((*x) == region) {
705                                 all_regions.erase (x);
706                                 changed = true;
707                         }
708
709                         x = xtmp;
710                 }
711
712                 region->set_playlist (boost::shared_ptr<Playlist>());
713         }
714
715         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
716                 ctmp = c;
717                 ++ctmp;
718
719                 if ((*c)->involves (r)) {
720                         unique_xfades.insert (*c);
721                         _crossfades.erase (c);
722                 }
723
724                 c = ctmp;
725         }
726
727         if (changed) {
728                 /* overload this, it normally means "removed", not destroyed */
729                 notify_region_removed (region);
730         }
731
732         return changed;
733 }
734
735 void
736 AudioPlaylist::crossfade_changed (Change)
737 {
738         if (in_flush || in_set_state) {
739                 return;
740         }
741
742         /* XXX is there a loop here? can an xfade change not happen
743            due to a playlist change? well, sure activation would
744            be an example. maybe we should check the type of change
745            that occured.
746         */
747
748         notify_modified ();
749 }
750
751 bool
752 AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
753 {
754         if (in_flush || in_set_state) {
755                 return false;
756         }
757
758         Change our_interests = Change (AudioRegion::FadeInChanged|
759                                        AudioRegion::FadeOutChanged|
760                                        AudioRegion::FadeInActiveChanged|
761                                        AudioRegion::FadeOutActiveChanged|
762                                        AudioRegion::EnvelopeActiveChanged|
763                                        AudioRegion::ScaleAmplitudeChanged|
764                                        AudioRegion::EnvelopeChanged);
765         bool parent_wants_notify;
766
767         parent_wants_notify = Playlist::region_changed (what_changed, region);
768
769         if ((parent_wants_notify || (what_changed & our_interests))) {
770                 notify_modified ();
771         }
772
773         return true;
774 }
775
776 void
777 AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
778 {
779         RegionLock rlock (this);
780
781         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
782                 nframes_t start, end;
783
784                 start = (*i)->position();
785                 end = start + (*i)->overlap_length(); // not length(), important difference
786
787                 if (frame >= start && frame <= end) {
788                         clist.push_back (*i);
789                 }
790         }
791 }
792
793 void
794 AudioPlaylist::foreach_crossfade (sigc::slot<void, boost::shared_ptr<Crossfade> > s)
795 {
796         RegionLock rl (this, false);
797         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
798                 s (*i);
799         }
800 }