Reset fades on regions copied from time ranges in other regions (#4035).
[ardour2.git] / libs / ardour / audio_playlist.cc
blobbb4bc9c0e3386f622b69b6145368edb2fdd17903
1 /*
2 Copyright (C) 2003 Paul Davis
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.
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.
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.
20 #include <algorithm>
22 #include <cstdlib>
24 #include "ardour/types.h"
25 #include "ardour/debug.h"
26 #include "ardour/configuration.h"
27 #include "ardour/audioplaylist.h"
28 #include "ardour/audioregion.h"
29 #include "ardour/crossfade.h"
30 #include "ardour/session.h"
31 #include "pbd/enumwriter.h"
33 #include "i18n.h"
35 using namespace ARDOUR;
36 using namespace std;
37 using namespace PBD;
39 namespace ARDOUR {
40 namespace Properties {
41 PBD::PropertyDescriptor<bool> crossfades;
45 void
46 AudioPlaylist::make_property_quarks ()
48 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
49 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
52 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
53 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
54 , _playlist (pl)
59 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
60 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
61 , _playlist (p._playlist)
67 CrossfadeListProperty *
68 CrossfadeListProperty::create () const
70 return new CrossfadeListProperty (_playlist);
73 CrossfadeListProperty *
74 CrossfadeListProperty::clone () const
76 return new CrossfadeListProperty (*this);
79 void
80 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
82 /* Crossfades are not written to any state when they are no
83 longer in use, so we must write their state here.
86 XMLNode& c = xfade->get_state ();
87 node.add_child_nocopy (c);
90 boost::shared_ptr<Crossfade>
91 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
93 XMLNodeList const c = node.children ();
94 assert (c.size() == 1);
95 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
99 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
100 : Playlist (session, node, DataType::AUDIO, hidden)
101 , _crossfades (*this)
103 #ifndef NDEBUG
104 const XMLProperty* prop = node.property("type");
105 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
106 #endif
108 add_property (_crossfades);
110 in_set_state++;
111 set_state (node, Stateful::loading_state_version);
112 in_set_state--;
115 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
116 : Playlist (session, name, DataType::AUDIO, hidden)
117 , _crossfades (*this)
119 add_property (_crossfades);
122 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
123 : Playlist (other, name, hidden)
124 , _crossfades (*this)
126 add_property (_crossfades);
128 RegionList::const_iterator in_o = other->regions.begin();
129 RegionList::iterator in_n = regions.begin();
131 while (in_o != other->regions.end()) {
132 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
134 // We look only for crossfades which begin with the current region, so we don't get doubles
135 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
136 if ((*xfades)->in() == ar) {
137 // We found one! Now copy it!
139 RegionList::const_iterator out_o = other->regions.begin();
140 RegionList::const_iterator out_n = regions.begin();
142 while (out_o != other->regions.end()) {
144 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
146 if ((*xfades)->out() == ar2) {
147 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
148 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
149 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
150 add_crossfade(new_fade);
151 break;
154 out_o++;
155 out_n++;
157 // cerr << "HUH!? second region in the crossfade not found!" << endl;
161 in_o++;
162 in_n++;
166 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
167 : Playlist (other, start, cnt, name, hidden)
168 , _crossfades (*this)
170 add_property (_crossfades);
172 /* Audio regions that have been created by the Playlist constructor
173 will currently have the same fade in/out as the regions that they
174 were created from. This is wrong, so reset the fades here.
177 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
178 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
179 assert (ar);
180 ar->set_default_fades ();
183 /* this constructor does NOT notify others (session) */
186 AudioPlaylist::~AudioPlaylist ()
188 _crossfades.clear ();
191 struct RegionSortByLayer {
192 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
193 return a->layer() < b->layer();
197 ARDOUR::framecnt_t
198 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
199 framecnt_t cnt, unsigned chan_n)
201 framecnt_t ret = cnt;
203 /* optimizing this memset() away involves a lot of conditionals
204 that may well cause more of a hit due to cache misses
205 and related stuff than just doing this here.
207 it would be great if someone could measure this
208 at some point.
210 one way or another, parts of the requested area
211 that are not written to by Region::region_at()
212 for all Regions that cover the area need to be
213 zeroed.
216 memset (buf, 0, sizeof (Sample) * cnt);
218 /* this function is never called from a realtime thread, so
219 its OK to block (for short intervals).
222 Glib::RecMutex::Lock rm (region_lock);
224 framepos_t const end = start + cnt - 1;
225 framecnt_t read_frames = 0;
226 framecnt_t skip_frames = 0;
227 _read_data_count = 0;
229 _read_data_count = 0;
231 RegionList* rlist = regions_to_read (start, start+cnt);
233 if (rlist->empty()) {
234 delete rlist;
235 return cnt;
238 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
239 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
240 vector<uint32_t> relevant_layers;
242 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
243 if ((*i)->coverage (start, end) != OverlapNone) {
244 relevant_regions[(*i)->layer()].push_back (*i);
245 relevant_layers.push_back ((*i)->layer());
249 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
250 if ((*i)->coverage (start, end) != OverlapNone) {
251 relevant_xfades[(*i)->upper_layer()].push_back (*i);
255 // RegionSortByLayer layer_cmp;
256 // relevant_regions.sort (layer_cmp);
258 /* XXX this whole per-layer approach is a hack that
259 should be removed once Crossfades become
260 CrossfadeRegions and we just grab a list of relevant
261 regions and call read_at() on all of them.
264 sort (relevant_layers.begin(), relevant_layers.end());
266 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
268 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
269 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
272 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
273 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
274 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
275 assert(ar);
276 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
277 _read_data_count += ar->read_data_count();
280 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
281 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
283 /* don't JACK up _read_data_count, since its the same data as we just
284 read from the regions, and the OS should handle that for us.
289 delete rlist;
290 return ret;
294 void
295 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
297 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
299 if (in_set_state) {
300 return;
303 if (r == 0) {
304 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
305 << endmsg;
306 return;
309 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
311 if ((*i)->involves (r)) {
312 i = _crossfades.erase (i);
313 } else {
314 ++i;
320 void
321 AudioPlaylist::flush_notifications (bool from_undo)
323 Playlist::flush_notifications (from_undo);
325 if (in_flush) {
326 return;
329 in_flush = true;
331 Crossfades::iterator a;
332 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
333 NewCrossfade (*a); /* EMIT SIGNAL */
336 _pending_xfade_adds.clear ();
338 in_flush = false;
341 void
342 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
344 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
345 set<boost::shared_ptr<Crossfade> > updated;
347 if (ar == 0) {
348 return;
351 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
353 Crossfades::iterator tmp;
355 tmp = x;
356 ++tmp;
358 /* only update them once */
360 if ((*x)->involves (ar)) {
362 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
364 if (u.second) {
365 /* x was successfully inserted into the set, so it has not already been updated */
366 try {
367 (*x)->refresh ();
370 catch (Crossfade::NoCrossfadeHere& err) {
371 // relax, Invalidated during refresh
376 x = tmp;
380 void
381 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
383 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
384 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
385 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
387 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
388 Crossfades::iterator tmp;
389 tmp = x;
390 ++tmp;
392 boost::shared_ptr<Crossfade> fade;
394 if ((*x)->_in == orig) {
395 if (! (*x)->covers(right->position())) {
396 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
397 } else {
398 // Overlap, the crossfade is copied on the left side of the right region instead
399 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
403 if ((*x)->_out == orig) {
404 if (! (*x)->covers(right->position())) {
405 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
406 } else {
407 // Overlap, the crossfade is copied on the right side of the left region instead
408 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
412 if (fade) {
413 _crossfades.remove (*x);
414 add_crossfade (fade);
416 x = tmp;
420 void
421 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
423 boost::shared_ptr<AudioRegion> other;
424 boost::shared_ptr<AudioRegion> region;
425 boost::shared_ptr<AudioRegion> top;
426 boost::shared_ptr<AudioRegion> bottom;
427 boost::shared_ptr<Crossfade> xfade;
428 RegionList* touched_regions = 0;
430 if (in_set_state || in_partition) {
431 return;
434 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
435 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
436 << endmsg;
437 return;
440 if (!norefresh) {
441 refresh_dependents (r);
445 if (!_session.config.get_auto_xfade()) {
446 return;
449 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
450 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
452 if (other == region) {
453 continue;
456 if (other->muted() || region->muted()) {
457 continue;
460 if (other->position() == r->position() && other->length() == r->length()) {
461 /* precise overlay of two regions - no xfade */
462 continue;
465 if (other->layer() < region->layer()) {
466 top = region;
467 bottom = other;
468 } else {
469 top = other;
470 bottom = region;
473 if (!top->opaque()) {
474 continue;
477 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
479 delete touched_regions;
480 touched_regions = 0;
482 try {
483 framecnt_t xfade_length;
484 switch (c) {
485 case OverlapNone:
486 break;
488 case OverlapInternal:
489 /* {=============== top =============}
490 * [ ----- bottom ------- ]
492 break;
494 case OverlapExternal:
496 /* [ -------- top ------- ]
497 * {=========== bottom =============}
500 /* to avoid discontinuities at the region boundaries of an internal
501 overlap (this region is completely within another), we create
502 two hidden crossfades at each boundary. this is not dependent
503 on the auto-xfade option, because we require it as basic
504 audio engineering.
507 xfade_length = min ((framecnt_t) 720, top->length());
509 if (top_region_at (top->first_frame()) == top) {
511 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
512 add_crossfade (xfade);
515 if (top_region_at (top->last_frame() - 1) == top) {
518 only add a fade out if there is no region on top of the end of 'top' (which
519 would cover it).
522 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
523 add_crossfade (xfade);
525 break;
526 case OverlapStart:
528 /* { ==== top ============ }
529 * [---- bottom -------------------]
532 if (_session.config.get_xfade_model() == FullCrossfade) {
533 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
534 if (touched_regions->size() <= 2) {
535 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
536 add_crossfade (xfade);
538 } else {
540 touched_regions = regions_touched (top->first_frame(),
541 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
542 top->length()));
543 if (touched_regions->size() <= 2) {
544 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
545 add_crossfade (xfade);
548 break;
549 case OverlapEnd:
552 /* [---- top ------------------------]
553 * { ==== bottom ============ }
556 if (_session.config.get_xfade_model() == FullCrossfade) {
558 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
559 if (touched_regions->size() <= 2) {
560 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
561 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
562 add_crossfade (xfade);
565 } else {
566 touched_regions = regions_touched (bottom->first_frame(),
567 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
568 bottom->length()));
569 if (touched_regions->size() <= 2) {
570 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
571 add_crossfade (xfade);
574 break;
575 default:
576 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
577 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
578 add_crossfade (xfade);
582 catch (failed_constructor& err) {
583 continue;
586 catch (Crossfade::NoCrossfadeHere& err) {
587 continue;
592 delete touched_regions;
595 void
596 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
598 Crossfades::iterator ci;
600 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
601 if (*(*ci) == *xfade) { // Crossfade::operator==()
602 break;
606 if (ci != _crossfades.end()) {
607 // it will just go away
608 } else {
609 _crossfades.push_back (xfade);
611 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
612 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
614 notify_crossfade_added (xfade);
618 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
620 if (g_atomic_int_get(&block_notifications)) {
621 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
622 } else {
623 NewCrossfade (x); /* EMIT SIGNAL */
627 void
628 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
630 Crossfades::iterator i;
631 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
633 xfade->in()->resume_fade_in ();
634 xfade->out()->resume_fade_out ();
636 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
637 _crossfades.erase (i);
642 AudioPlaylist::set_state (const XMLNode& node, int version)
644 XMLNode *child;
645 XMLNodeList nlist;
646 XMLNodeConstIterator niter;
648 in_set_state++;
650 Playlist::set_state (node, version);
652 freeze ();
654 nlist = node.children();
656 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
658 child = *niter;
660 if (child->name() != "Crossfade") {
661 continue;
664 try {
665 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
666 _crossfades.push_back (xfade);
667 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
668 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
669 NewCrossfade(xfade);
672 catch (failed_constructor& err) {
673 // cout << string_compose (_("could not create crossfade object in playlist %1"),
674 // _name)
675 // << endl;
676 continue;
680 thaw ();
681 in_set_state--;
683 return 0;
686 void
687 AudioPlaylist::clear (bool with_signals)
689 _crossfades.clear ();
690 Playlist::clear (with_signals);
693 XMLNode&
694 AudioPlaylist::state (bool full_state)
696 XMLNode& node = Playlist::state (full_state);
698 if (full_state) {
699 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
700 node.add_child_nocopy ((*i)->get_state());
704 return node;
707 void
708 AudioPlaylist::dump () const
710 boost::shared_ptr<Region>r;
711 boost::shared_ptr<Crossfade> x;
713 cerr << "Playlist \"" << _name << "\" " << endl
714 << regions.size() << " regions "
715 << _crossfades.size() << " crossfades"
716 << endl;
718 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
719 r = *i;
720 cerr << " " << r->name() << " @ " << r << " ["
721 << r->start() << "+" << r->length()
722 << "] at "
723 << r->position()
724 << " on layer "
725 << r->layer ()
726 << endl;
729 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
730 x = *i;
731 cerr << " xfade ["
732 << x->out()->name()
733 << ','
734 << x->in()->name()
735 << " @ "
736 << x->position()
737 << " length = "
738 << x->length ()
739 << " active ? "
740 << (x->active() ? "yes" : "no")
741 << endl;
745 bool
746 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
748 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
750 if (!r) {
751 return false;
754 bool changed = false;
755 Crossfades::iterator c, ctmp;
756 set<boost::shared_ptr<Crossfade> > unique_xfades;
759 RegionLock rlock (this);
761 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
763 RegionList::iterator tmp = i;
764 ++tmp;
766 if ((*i) == region) {
767 regions.erase (i);
768 changed = true;
771 i = tmp;
774 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
776 set<boost::shared_ptr<Region> >::iterator xtmp = x;
777 ++xtmp;
779 if ((*x) == region) {
780 all_regions.erase (x);
781 changed = true;
784 x = xtmp;
787 region->set_playlist (boost::shared_ptr<Playlist>());
790 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
791 ctmp = c;
792 ++ctmp;
794 if ((*c)->involves (r)) {
795 unique_xfades.insert (*c);
796 _crossfades.erase (c);
799 c = ctmp;
802 if (changed) {
803 /* overload this, it normally means "removed", not destroyed */
804 notify_region_removed (region);
807 return changed;
810 void
811 AudioPlaylist::crossfade_changed (const PropertyChange&)
813 if (in_flush || in_set_state) {
814 return;
817 /* XXX is there a loop here? can an xfade change not happen
818 due to a playlist change? well, sure activation would
819 be an example. maybe we should check the type of change
820 that occured.
823 notify_contents_changed ();
826 bool
827 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
829 if (in_flush || in_set_state) {
830 return false;
833 PropertyChange our_interests;
835 our_interests.add (Properties::fade_in_active);
836 our_interests.add (Properties::fade_out_active);
837 our_interests.add (Properties::scale_amplitude);
838 our_interests.add (Properties::envelope_active);
839 our_interests.add (Properties::envelope);
840 our_interests.add (Properties::fade_in);
841 our_interests.add (Properties::fade_out);
843 bool parent_wants_notify;
845 parent_wants_notify = Playlist::region_changed (what_changed, region);
847 if (parent_wants_notify || (what_changed.contains (our_interests))) {
848 notify_contents_changed ();
851 return true;
854 void
855 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
857 RegionLock rlock (this);
859 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
860 framepos_t const start = (*i)->position ();
861 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
863 if (frame >= start && frame <= end) {
864 clist.push_back (*i);
869 void
870 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
872 RegionLock rl (this, false);
873 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
874 s (*i);
878 void
879 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
881 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
882 add_crossfade (*i);
885 /* don't remove crossfades here; they will be dealt with by the dependency code */
888 boost::shared_ptr<Crossfade>
889 AudioPlaylist::find_crossfade (const PBD::ID& id) const
891 Crossfades::const_iterator i = _crossfades.begin ();
892 while (i != _crossfades.end() && (*i)->id() != id) {
893 ++i;
896 if (i == _crossfades.end()) {
897 return boost::shared_ptr<Crossfade> ();
900 return *i;