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